// import rasterizeHTML from '@/assets/js/rasterizehtml/rasterizeHTML.allinone.js';
import automatic from './automatic.js';
(function(window) {
    /* 初始配置（外部使用）
    */
    let $init = {
        selector:"",     // 必填，容器ID
        data:[],         // 必填，初始节点集
        width:null,      // 容器宽
        height:null,     // 容器高
        canvasScale:10,  // 无限画布比例 例canvasScale=2 2倍于容器大小
        scale:{          // 鼠标缩放比例
            max:1.9,
            min:0.5,
        },
        pull:false,
        initFn:()=>{},  // 节点更新后需要重新初始化的操作（自定义）
        editable:true,  // 节点是否可编辑
        hawkEye:{       // 鹰眼
            open:false,
            pid:null,
        },       
        style:{         // 自定义样式
            line:{
                colors:['#207CFC'],
                size:2,
            },
            contactLine:{
                colors:['#F6753B'],
                size:3,
            },
            node:{
                class:'',           // 所有节点的样式
                cls:[],             // 多节点样式
                space_x:80,         // 节点之间水平间距
                space_y:28,         // 节点之间垂直间距
                pull:{},
                hasIcon:false       // 节点是否有图标
            },
            contextMenu:{
                class:''
            },
            noSelectionArea:{       // 禁选区域样式
                bgColor:"rgba(255,255,255,.1)",
                borderWidth:100 ,   // 左右额外区域
            },
        },
        nodeLimit:-1,// 固定尺寸-全局
        nodePriorityDefaultCls:{   // 新增结节初始化样式
            nodeStyleCls:"",
            nodeIcon:"",
        },
        nodePriorityCls:{
            rule:{
                nodeStyleCls:"",
                nodeIcon:"",
            },
            compute:{
                nodeStyleCls:"",
                nodeIcon:"",
            },
            resource:{
                nodeStyleCls:"",
                nodeIcon:"",
            },
            currency:{
                nodeStyleCls:"",
                nodeIcon:"",
            },
        },
        nodeStateCls:{          // 节点状态样式
            ing:"",
            success:"",
            fail:""
        },
        contextMenu:{   // 自定义右键
            addItem:[],
        }
    }
    let $s      = null; // 最外层容器元素
    let $e      = null; // 画布容器
    let $e_beginX = 0; // 画布容器的初始X值
    let $nEls   = null; // 节点元素集
    let $scale  = 1; // 缩放比例
    let $scaleOut  = 1; // 缩放比例-外部使用
    let $w_s    = 0, // 最外层容器-宽
        $h_s    = 0, // 最外层容器-高
        $w_e    = 0, // 画布容器-宽
        $h_e    = 0; // 画布容器-高
    let $begin_xy = []; // 初始XY值
    let $isDraw = true;// 是否可以拖拽
    
    // 节点的事件操作
    let $nodeEvt = {
        draw:false,
        cntMenu:false,
        contactLine:false,
    };

    // 本地数据库-索引为id
    let __DB__  = {
        nodes:{},// 普通节点
        frees:{},// 自由节点
        lines:{},// 线
        contactLines:{},// 关联线
    };

    // 本地数据集-索引是下标
    let __Arr__ = {
        nodes:[],// 普通节点
        frees:[],// 自由节点
        lines:[],// 线
        contactLines:[],// 关联线
        temporaryLine:{},// 临时线
        contactLinesSVG:[],// 关联线svg
    }

    // 普通节点的多维树型结构
    let $nodesMany          = [];// 树节点多维数据
    let $nodesMany_frees    = [];// 自由节点多维数据
    let $nodesMany_ids      = {};// 节点的子节点ID列表

    let $myEvent = {
        scaleChange         :null,// 缩放完成
        temporaryLineUp     :null,// 连线完成
        undoRedoOpt         :null,// 撤消重做需要的事件回调
        nodesReset          :null,// 节点重置需要的事件回调
        nodeAdd             :null,// 新增节点
        nodeDel             :null,// 删除节点
        nodeUpdata          :null,// 更新节点
        nodeClick           :null,// 单击节点
        nodeDblclick        :null,// 双击节点
        nodeDrag            :null,// 拖动节点
        nodeCntMenu         :null,// 右键节点
        cntMenu             :null,// 右键
        load                :null,// 加载完成
        contactLineEvt      :null,// 关联线连接成功
        
    };

    // 操作字典
    let $opt = {
        line                : false,
        nodeAdd             : null,// 新增节点
        nodeDel             : null,// 删除节点
        nodeUpdata          : null,// 更新节点
        nodeClick           : null,// 单击节点
        nodeDblclick        : null,// 双击节点
        nodeDrag            : null,// 拖动节点
        nodeCntMenu         : null,// 右键节点
        cntMenu             : null,// 右键
        load                : null,// 加载完成
        contactLineEvt      : null,// 关联线连接成功
        nodesReset          : null,// 节点重置
    };

    // 初始化xmind
    window.$xmind = class {
        constructor(res){
            if(!res)return;
            $scale = 1;
            
            let selector = res.selector;
            let data     = res.data;

            // 创建容器
            $s = document.getElementById(selector);
            let main = document.createElement('div');
            $e = document.getElementById('canvas');
            if($e)$s.removeChild($e);
            main.setAttribute('id','canvas');
            main.setAttribute('class','xmd-dCanvas');
            $s.appendChild(main);
            

            // 创建画布容器
            $e = document.getElementById('canvas');
            $e.oncontextmenu = (e)=>{
                e.stopPropagation();
                e.preventDefault();
            }

            let svgAllmain = document.createElement('div');
            svgAllmain.setAttribute('id','svgAllmain')
            $e.appendChild(svgAllmain);
            
            $init = __tool__.extends($init,res);
            __cntMenu__.addItemfn();// 初始化右键

            this.style();//容器的尺寸

            // if(data.length > 0){
                __canvas__.load({items:data});// 初始画布数据
            // }

            $e_beginX = parseInt($e.style.left); // 画布容器的初始X值

            
            setTimeout(()=>{
                $scale = $scaleOut;
                $e.style.transform = `scale(${$scaleOut})`;
            })
            
        }

        //容器的尺寸
        style(){
            let w = $init.width || parseFloat($s.getBoundingClientRect().width),
                h = $init.height || parseFloat($s.getBoundingClientRect().height);
            $s.style.width  = w + "px";
            $s.style.height = h + "px";

            let m = $init.canvasScale;//无限画布比例 例m=2 2倍于容器大小
            $w_s = w;
            $h_s = h;
            $w_e = w*m;
            $h_e = h*m;

            $begin_xy = [(w*m-w)/2,(h*m-h)/2];

            $e.style.cssText = `
                top:-${(h*m-h)/2}px;
                left:-${(w*m-w)/2}px;
                width:${w*m}px;
                height:${h*m}px;
            `;
        }

        /*
            新增节点
            xmid.addNode({
                pid     : "",// 必填
                label   : "",// 必填
                left    : false,  // 必填
                html    : ``,
                fn      : ()=>{}
            });
        */
        addNode(res){
            let obj = {
                pid     : res.pid,
                id      : res.id||"new_"+(new Date).getTime(),
                root    : false,
                label   : res.label||"",
                left    : res.left||false,  
                type    : "text", //暂未开发，请使用HTML自定义内容
                html    : res.html||null,
            };

            let rb = {
                msg:"success",
                data:null,
            }
            
            if(__DB__.nodes[obj.id]){
                rb.msg = "fail";
                console.log("新增节点失败");
                if(typeof res.fn)res.fn(rb);
                return;
            }
            
            __Arr__.nodes.push(obj);
            __DB__.nodes[obj.id] = __tool__.deepCopy(obj);
            __canvas__.load();

            window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
            window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            rb.data = __DB__.nodes[obj.id];
            if(typeof res.fn)res.fn(rb);
        }

        /*
            删除节点
            xmid.delNode({
                id      : "", // 必填
                fn      : ()=>{}
            });
        */
        delNode(res){
            let id = res.id;
            let rb = {
                msg:"success"
            }

            if(!!id){
                if(!__DB__.nodes[id]){
                    rb.msg = "fail";
                    console.log("删除节点失败");
                    if(res.fn && typeof res.fn)res.fn(rb);
                    return;
                }

                let the = __DB__.nodes[id];
                __Arr__.nodes.splice(the.index,1);
                delete the[id];
                
                __Arr__.nodes.forEach((el,i)=>{
                    el.index = i;
                    __DB__.nodes[el.id].index = i;
                })

                __canvas__.load();
                window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
                if(res.fn && typeof res.fn)res.fn(rb);
            }
            else{
                rb.msg = "fail";
                if(typeof res.fn)res.fn(rb);
            }
        }

        // 重置节点集
        setNodes(items,fn,sign=true){
            if(!items)return;
            if(!(items instanceof Array))return;
            if(items.length === 0)return;
            automatic.clear();// 清空关联线

            let arr = items.map((el,index)=>{
                el.index = index;
                el.html = el.html||"";

                let nodeLimit = -1;

                if(el.nodeLimit){
                    nodeLimit = el.nodeLimit;
                }
                else{
                    if(el.base&&!el.base.size){
                        nodeLimit = parseInt(el.base.sizeContent);
                    }
                }

                el.styleInit = {
                    nodeIcon            :el.nodeIcon||"",           // 节点图标
                    nodeStyleCls_root   :el.nodeStyleCls_root||"",  // 节点样式-根节点
                    nodeStyleCls        :el.nodeStyleCls||"",       // 节点样式
                    priority            :el.priority||0,
                    nodeLimit           :nodeLimit,
                }

                if(typeof el.init === "string" && el.init !== ""){
                    el.init = JSON.parse(el.init);
                }

                return el;
            })

            if(typeof fn === "function"){
                $init.initFn = fn;
            }

            __Arr__.nodes = __tool__.deepCopy(arr);
            __canvas__.load({isInitial:true});
            if(sign){
                // window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            }
        }

        // 更新节点状态
        setNodeState(item){
            if(!item)return;
            if(Object.keys(item).length === 0)return;

            let id      = item.id,
                state   = item.state;

            if(!id)return;
            let node = null;
            let nodes = document.querySelectorAll('.xmd-node');
            for(let el of nodes){
                let id_el = el.getAttribute('id');
                if(id === id_el){
                    node = el;
                    break;
                }
            }

            if(!node)return;

            let cls = "";
            if(state === 0){
                cls = $init.nodeStateCls.fail;
            }
            else if(state === 1){
                cls = $init.nodeStateCls.ing;
            }
            else if(state === 2){
                cls = $init.nodeStateCls.success;
            }

            let stateEl = node.firstElementChild.lastElementChild;
            let baseCls = stateEl.classList[0];
             stateEl.setAttribute("class",baseCls+" "+cls);
            
        }

        // 刷新
        refresh(type){
            if(type === "isInitial"){
                __canvas__.load({isInitial:true});
            }
            else{
                __canvas__.load();
            }
            
        }

        // 获取树节点集
        getNodesTree(){
            if(__Arr__.nodes.length === 0)return [];
            let arr = __Arr__.nodes.map(item=>{
                return this.getRebackData(item);
            });
            return arr;
        }

        // 获取自由节点集
        getNodesFree(){
            if(__Arr__.frees.length === 0)return [];
            let arr = __Arr__.frees.map(item=>{
                return this.getRebackData(item);
            });
            return arr;
        }

        // 获取单个节点 getNode({id:""});
        getNode(res){
            let id = res.id||null;
            if(!id)return;

            return this.getRebackData(__DB__.nodes[id]);
        }

        // 过滤返回值
        getRebackData(item){
            let obj = {
                id          : item.id,
                pid         : item.pid,
                root        : item.root,
                label       : item.label,
                type        : item.type,
                nodeType    : item.nodeType,
                left        : item.left,
                index       : item.index,
                init        : item.init,
                priority    : item.styleInit.priority,
                // indexMany   : item.indexMany,
            }
            return obj;
        }

        // 居中
        setCenter(){
            $e.style.left = -$begin_xy[0] + "px";
            $e.style.top  = -$begin_xy[1] + "px";
        }

        // 放大
        enlarge(scale){
            if(!scale)$scale += 0.1;

            if($scale > 2){
                $scale = 2;
                $scaleOut = $scale;
                return;
            };

            $scaleOut = $scale;
            $e.style.transform = `scale(${$scale})`;
        }

        // 缩小
        narrow(scale){
            if(!scale)$scale -= 0.1;

            if($scale < 0.4){
                $scale = 0.4;
                $scaleOut = $scale;
                return
            };

            $scaleOut = $scale;
            $e.style.transform = `scale(${$scale})`;
        }

        // 缩放
        setZoom(res){
            if(!res)return;

            if(res === 'enlarge'){
                fn_enlarge();
                return;
            }

            if(res === 'narrow'){
                fn_narrow();
                return;
            }

            if(typeof res === 'number'){
                if(res >= 0.4 && res <= 2){
                    $scale = parseFloat(res);
                    $scaleOut = $scale;
                    $e.style.transform = `scale(${res})`;
                }
            }

            function fn_enlarge(){
                $scale = parseFloat($scale);
                $scale += 0.1;
                $scale = $scale.toFixed(1);
                if($scale > 2){
                    $scale = 2;
                    $scaleOut = $scale;
                    return
                };
                $scaleOut = $scale;
                $e.style.transform = `scale(${$scale})`;
            }

            function fn_narrow(){
                $scale = parseFloat($scale);
                $scale -= 0.1;
                $scale = $scale.toFixed(1);
                if($scale < 0.4){
                    $scale = 0.4;
                    $scaleOut = $scale;
                    return
                };
    
                $scaleOut = $scale;
                $e.style.transform = `scale(${$scale})`;
            }
        }

        // 鹰眼  open pid
        hawkEye(open,pid){
            if(typeof open !== 'boolean'){
                console.error('hawEye 第一个参数必须是boolean');
                return;
            }
            $init.hawkEye.open = open;
            $init.hawkEye.pid  = pid;
            __canvas__.hawkEye();
        }

        // 展开/收起
        setPull(sign){
            $init.pull = sign||!$init.pull;
            this.refresh('isInitial');
        }

        // 开启/关闭拖拽节点
        switchDraw(sign=true){
            $isDraw = sign;
        }

        // 销毁
        destroyed(fn){
            $s.remove();
            if(fn)fn();
            this.off();
        }

        // 重置右键菜单
        resetCntMenu(obj){
            let has     = obj.has,
                type    = obj.type||"node";
            __cntMenu__.hasDefaultOpts.has = has;
            __cntMenu__.hasDefaultOpts.type = type;
        }

        // 清空
        clear(fn){
            let svgAllmain = document.getElementById('svgAllmain');
            svgAllmain.innerHTML = "";
            $e.innerHTML = "";
            $e.appendChild(svgAllmain)
            if(fn)fn();
        }

        // 创建关联线：需要配合右键菜单使用
        contectLine(id){
            if(!id)return;
            automatic.isDraw = true;
            automatic.startName = id;
            automatic.drawDirectionLine({
                id,
                ids:__DB__.nodes,
                xy:[__cntMenu__.x,__cntMenu__.y]
            });

            // window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            // window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
        }

        // 选中关联线
        contactLineChecked(res){
            let s = res.startId,
                e = res.endId;
            // if(!s || !e)return;
            
            automatic.checkedLine(s,e);
        }

        // 绘制关联线
        drawContactLine(res){
            let s           = res.startId,
                e           = res.endId;
            if(!s || !e)return;

            if(!__DB__.nodes[s] || !__DB__.nodes[e])return;

            automatic.startName = s;
            automatic.endName = e;
            
            automatic.contect({
                nodes   : __Arr__.nodes,
                ids     : __DB__.nodes,
                canvas  : document.getElementById('automaticConnection')
            })
        }

        // 新增关联线的事件
        addContactListEVT(){
            window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
        }

        // 删除关联线
        delContactLine(res){
            let s = res.startId,
                e = res.endId,
                isUndoOpt = res.isUndoOpt||false;
            if(!s || !e)return;

            automatic.delLine(s,e);

            if(!isUndoOpt){
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
                window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
            }
            
        }

        // 取消关联
        cancelContactLine(){
            automatic.cancelDirectionLine();
        }

        // 高亮部分节点
        highlightNodes(items){

            __DB__.lines.forEach(el=>{
                el.execute = false;
                el.color   = "";
            })

            automatic.hideLineAll();

            if(!items || items.length === 0)return;

            let lines = __DB__.lines;
            let automaticLines = automatic.pathsOnlyName;

            formatLines(items,lines);
            
            formatLines2.call(this,items,automaticLines);

            function formatLines(arr1,arr2){
                
                let arr = [];
                arr1.forEach(el_1=>{
                    for(let el_2 of arr1){
                        if(el_1.id === el_2.id)continue;

                        arr.push({
                            s:el_1.id,
                            e:el_2.id
                        })
                    }
                })

                arr.forEach(el1=>{
                    let s = el1.s,
                        e = el1.e;

                    for(let el2 of arr2){
                        if(s === el2.s.id && e === el2.e.id){
                            el2.execute = true;
                            el2.color   = "#fb685c";
                            break;
                        }
                    }
                })
            }

            function formatLines2(arr1,arr2){
                let arr = [];
                arr1.forEach(el_1=>{
                    for(let el_2 of arr1){
                        if(el_1.id === el_2.id)continue;

                        arr.push({
                            s:el_1.id,
                            e:el_2.id
                        })
                    }
                })

                

                arr.forEach(el1=>{
                    let s = el1.s,
                        e = el1.e;

                    for(let el2 of arr2){
                        if(s === el2.s && e === el2.e){
                            automatic.showLine(s,e);
                            break;
                        }
                    }
                })
            }

            line_draw();

            

            const nodes = document.querySelectorAll(".xmd-node");
            if(nodes.length === 0)return;
            for(let el of nodes){
                let cls_el = el.getAttribute('class');
                if(cls_el.indexOf('xmd-node-highlight-'+__DB__.nodes[el.id].type) > -1){
                    el.classList.remove('xmd-node-highlight-'+__DB__.nodes[el.id].type);
                }
            }

            if(items.length === 0)return;

            items.forEach(el=>{
                let parentNode = document.getElementById(el.id)
                parentNode.classList.add("xmd-node-highlight-"+__DB__.nodes[el.id].type);
            });


            // 绘制线
            function line_draw(){
                $e = document.getElementById('canvas');
                let w = $e.getBoundingClientRect().width;
                let h = $e.getBoundingClientRect().height;

                let colors = $init.style.line.colors;// 颜色配置
                
                let len = colors.length;
                let lineSize = $init.style.line.size;
                let html = '';
                __DB__.lines.forEach(el=>{
                    // 临时写的
                    let color = colors[__format__.num];

                    if(el.execute)color=el.color;
                    if(len > 1){
                        if(__format__.num === len - 1){
                            __format__.num = 0;
                        }
                        else{
                            __format__.num++;
                        }
                    }

                    let line = __format__.line_path(el);
                    html += `
                        <path
                            fill="none"
                            stroke="${color}"
                            opacity="1"
                            stroke-width="${lineSize}"
                            pointer-events="stroke"
                            d="${line}"
                            ></path>
                    `;
                });

                let xmdSveEl = document.getElementById('svg');
                xmdSveEl.innerHTML = html;


            }
        


            // __canvas__.load();
        }

        // 创建拖拽临时对象
        /*
            createdDraw({
                id,     //必填
                label,  //必填
                e,      //必填-dom元素
                html,
            })
        */
        createdDraw(res){
            if(!res || !res.id)return false;

            let id      = res.id,
                label   = res.label,
                type    = "other",
                e       = res.e;
            
            res.nodeType = res.type;

            res.type = type;

            __draw__.drawObj(res);
            __draw__.drawType       = 'free';
            __draw__.drawOldData    = res;
            __draw__.drawNewNode    = true;

            // +-按钮事件穿透
            let pull_r = document.querySelectorAll('.xmd-node-pull-r');
            let pull_l = document.querySelectorAll('.xmd-node-pull-l');

            // +-按钮事件穿透
            pull_r.forEach(el=>{
                el.style.pointerEvents = "none";
            });

            pull_l.forEach(el=>{
                el.style.pointerEvents = "none";
            });
            

            let ox = e.offsetX,
                oy = e.offsetY;
            document.onmousemove = (e)=>{
                __draw__.drawObj({
                    x:e.clientX - ox,
                    y:e.clientY - oy,
                });// 设置拖拽对象位置
                
                __draw__.drawEvent  = res.e;
            }
        
            document.onmouseup = (ev)=>{
                document.onmousemove    = null;
                document.onmouseup      = null;
                $s.onmouseout           = null;
                $s.onmouseover          = null;
                __draw__.x = ev.offsetX - e.offsetX;
                __draw__.y = ev.offsetY - e.offsetX;
                
                __draw__.drawObj({type:"del"}); // 删除拖拽对象
                __draw__.up();                  // 拖拽结束
                __draw__.clear();               // 清理拖拽
                __draw__.drawNewNode = false;

                const nodesIn = document.querySelectorAll('.xmd-node-in');
                for(let el of nodesIn){
                    el.style.display = "none";
                }

                // +-按钮事件穿透
                pull_r.forEach(el=>{
                    el.style.pointerEvents = "auto";
                });

                pull_l.forEach(el=>{
                    el.style.pointerEvents = "auto";
                });
            }

            $s.onmouseout = (ev)=>{
                __draw__.hasInCanvas = false;
            }

            $s.onmouseover = (ev)=>{
                __draw__.hasInCanvas = true;
            }
        }

        // 设置右键功能
        // 设置右键选项
        /*
            res:{
                type:"canvas",// 类型（canvas、node、contactLine）
                items:[
                    {label:"",id:"",name:"canvas"}
                ],x
            }
        */
        setContentMenu(res){
            __cntMenu__.setItemfn(res);
        }

        // 设置居中显示
        canvasCenter(num=0){
            if(__Arr__.nodes.length === 0)return;
            for(let el of __Arr__.nodes){
                if(el.left)return;
            }

            let rootW = __DB__.nodes.root.init.size.w;
            let sw  = parseInt($w_s);
            let s2w  = parseInt(sw/2);
            let arr = [];
            __Arr__.nodes.forEach((el,i)=>{
                arr.push({
                    id:el.id,
                    w:parseInt(el.init.size.w),
                    x:parseInt(el.init.poi.x),
                })
            });

            arr.sort(sortData);

            let len = arr.length;
            let s = arr[0];
            let e = arr[len-1];
            let nodesMaxW = e.x - s.x + e.w;
            let outsideW = 0;
            if(s2w > nodesMaxW){
                outsideW = nodesMaxW/2;
            }
            else if(s2w < nodesMaxW && sw > nodesMaxW){
                outsideW = nodesMaxW/2;
            }
            else if(sw < nodesMaxW){
                outsideW = sw/2;
            }

            let left = $e_beginX;
            let newLeft = left  + rootW/2 - outsideW;

            if(sw< nodesMaxW){
                newLeft = newLeft + num;
            }
            else if(s2w < nodesMaxW && sw > nodesMaxW && (sw - nodesMaxW)/2 < num){
                newLeft = newLeft + num;
            }

            $e.style.left = newLeft + "px";
            

            // 排序
            function sortData(a,b){
                return a.x - b.x
            }
        }

        // on 操作回调事件---
        on(eventName,fn){
            let _this = this;
            if(!eventName)return;
            if(eventName === "scaleChange"){
                scaleChange();
                return;
            }

            if(eventName === "temporaryLineUp"){
                temporaryLineUp();
                return;
            }

            // 撤消重做需要的事件回调
            if(eventName === "undoRedoOpt"){
                undoRedoOpt();
                return;
            }

            // 节点重置需要的事件回调
            if(eventName === "nodesReset"){
                nodesReset();
                return;
            }

            // 新增节点
            if(eventName === "nodeAdd"){
                nodeAdd();
                return;
            }

            // 删除节点
            if(eventName === "nodeDel"){
                nodeDel();
                return;
            }

            // 双击节点
            if(eventName === "nodeDblclick"){
                nodeDblclick();
                return;
            }

            // 双击节点
            if(eventName === "nodeClick"){
                nodeClick();
                return;
            }

            // 拖动节点
            if(eventName === "nodeDrag"){
                nodeDrag();
                return;
            }

            // 更新节点
            if(eventName === "nodeUpdata"){
                nodeUpdata();
                return;
            }

            // 右键节点
            if(eventName === "nodeCntMenu"){
                nodeCntMenu();
                return;
            }

            // 右键
            if(eventName === "cntMenu"){
                cntMenu();
                return;
            }

            // 关联线连接成功
            if(eventName === "contactLineEvt"){
                contactLineEvt();
                return;
            }

            
            
            function scaleChange(){
                if(!fn)return;
                _this.evtFn_scaleChange = function(event){
                    fn(event,{
                        scale:$scale
                    })
                };
                window.addEventListener('scaleChange', _this.evtFn_scaleChange);
            }

            function temporaryLineUp(){
                if(!fn)return;
                _this.evtFn_temporaryLineUp = function(event){
                    fn(event,{
                        temporaryLine:__Arr__.temporaryLine
                    })
                };
                window.addEventListener('temporaryLineUp', _this.evtFn_temporaryLineUp);
            }

            function undoRedoOpt(){
                if(!fn)return;
                _this.evtFn_undoRedoOpt = function(event){
                    fn(event,{
                        msg:"已操作"
                    })
                };
                window.addEventListener('undoRedoOpt', _this.evtFn_undoRedoOpt);
            }

            function nodesReset(){
                if(!fn)return;
                _this.evtFn_nodesReset = function(event){
                    fn(event,{
                        msg:"已重置"
                    })
                };
                window.addEventListener('nodesReset', _this.evtFn_nodesReset);
            }

            function nodeAdd(){
                if(!fn)return;
                _this.evtFn_nodeAdd = function(event){
                    fn(event,{
                        msg:"新增节点",
                        data:$opt.nodeAdd
                    })
                };
                window.addEventListener('nodeAdd', _this.evtFn_nodeAdd);
            }

            function nodeDel(){
                if(!fn)return;
                _this.evtFn_nodeDel = function(event){
                    fn(event,{
                        msg:"删除节点",
                        data:$opt.nodeDel
                    })
                };
                window.addEventListener('nodeDel', _this.evtFn_nodeDel);
            }

            function nodeClick(){
                if(!fn)return;
                _this.evtFn_nodeClick = function(event){
                    fn(event,{
                        msg:"单击节点",
                        data:$opt.nodeClick
                    })
                };
                window.addEventListener('nodeClick', _this.evtFn_nodeClick);
            }

            function nodeDblclick(){
                if(!fn)return;
                _this.evtFn_nodeDblclick = function(event){
                    fn(event,{
                        msg:"双击节点",
                        data:$opt.nodeDblclick
                    })
                };
                window.addEventListener('nodeDblclick', _this.evtFn_nodeDblclick);
            }

            function nodeDrag(){
                if(!fn)return;
                _this.evtFn_drawOldData = function(event){
                    fn(event,{
                        msg:"拖动节点",
                        data:$opt.nodeDrag,
                        dragInfo:__draw__.drawOldData,
                    })
                };
                window.addEventListener('nodeDrag', _this.evtFn_drawOldData);
            }

            function nodeUpdata(){
                if(!fn)return;
                _this.evtFn_nodeUpdata = function(event){
                    fn(event,{
                        msg:"更新节点",
                    })
                };
                window.addEventListener('nodeUpdata', _this.evtFn_nodeUpdata);
            }

            function nodeCntMenu(){
                if(!fn)return;
                _this.evtFn_nodeCntMenu = function(event){
                    fn(event,{
                        msg:"右健节点",
                        data:$opt.nodeCntMenu
                    })
                };
                window.addEventListener('nodeCntMenu', _this.evtFn_nodeCntMenu);
            }

            function cntMenu(){
                if(!fn)return;
                _this.evtFn_cntMenu = function(event){
                    fn(event,{
                        msg:"右健",
                        data:$opt.cntMenu
                    })
                };
                window.addEventListener('cntMenu', _this.evtFn_cntMenu);
            }
            
            function contactLineEvt(){
                if(!fn)return;
                // automatic.cancelDirectionLine();
                _this.evtFn_contactLineEvt = function(event){
                    fn(event,{
                        msg:"关联线连接成功",
                        data:$opt.contactLineEvt
                    })
                };
                window.addEventListener('contactLineEvt', _this.evtFn_contactLineEvt);
            }
        }

        off(eventName='all'){
            if(eventName === 'all'){
                for(let k in $myEvent){
                    window.removeEventListener(k, this['evtFn_'+k]);
                }
            }else if($myEvent[eventName]){
                window.removeEventListener(eventName, this['evtFn_'+eventName]);
            }
        }
        
    }

    // 画布模块
    class xCanvas {
        constructor(){
            this.x = 0;
            this.y = 0;
            this.eye = {
                el:null,
                box:null,
                scaleW:null,
                scaleH:null,
            };
            this.canvas_x = null;
            this.canvas_y = null;
        }
        // 初始化画面内容
        load(res){
            let items = res&&res.items||__Arr__.nodes;
            let type = res&&res.type||"init";
            let isInitial = res&&res.isInitial||"no";//判断是否要重置

            for(let i in items){
                items[i].index = parseFloat(i);//判断是否要重置
            }

            if(type === 'init'){
                this.clear();// 清除画布内容
                __format__.clear();// 清除画布的内容
                for(let i in items){
                    items[i].isInitial = isInitial;//判断是否要重置
                    __format__.nodeInit(items[i],i);
                }
            }

            this.divEvent();            // 注册自定义事件
            this.reset();               // 重置节点数据
            this.auto();                // 开始无限画布
            this.option();              // 操作画布
            
            __node__.evt();             // 初始化节点事件
            __line__.show_contact();    // 显示关联线

            __opt__.pull();             // 初始化节点的展开收起功能
            this.hawkEye();             // 鹰眼
            this.canvas_x = parseFloat($e.style.left);
            this.canvas_y = parseFloat($e.style.top);

            $init.initFn();             // 初始化完成后需要加载的外部功能

            // 虚线重置
            automatic.reset({
                nodes:__Arr__.nodes,
                ids:__DB__.nodes,
            })
        }

        // 注册自定义事件
        divEvent(){
            $myEvent.temporaryLineUp    = new Event('temporaryLineUp'); // 连线完成
            $myEvent.scaleChange        = new Event('scaleChange');     // 缩放完成
            $myEvent.undoRedoOpt        = new Event('undoRedoOpt');     // 撤消重做需要的事件回调
            $myEvent.nodesReset         = new Event('nodesReset');      // 节点重置
            $myEvent.nodeAdd            = new Event('nodeAdd');         // 新增节点
            $myEvent.nodeDel            = new Event('nodeDel');         // 删除节点
            $myEvent.nodeUpdata         = new Event('nodeUpdata');      // 更新节点
            $myEvent.nodeClick          = new Event('nodeClick');       // 单击节点
            $myEvent.nodeDblclick       = new Event('nodeDblclick');    // 双击节点
            $myEvent.nodeDrag           = new Event('nodeDrag');        // 拖动节点
            $myEvent.nodeCntMenu        = new Event('nodeCntMenu');     // 右健节点
            $myEvent.cntMenu            = new Event('cntMenu');         // 右健
            $myEvent.contactLineEvt     = new Event('contactLineEvt');  // 关联线连接成功
            
            
        }

        // 解析生成节点数据
        reset(){
            $nEls = document.querySelectorAll('.xmd-node');
            for(let el of $nEls){
                let n_w = el.getBoundingClientRect().width,
                    n_h = el.getBoundingClientRect().height,
                    id  = el.getAttribute('id');

                    n_w = n_w/$scale;
                    n_h = n_h/$scale;
                
                let i = __DB__.nodes[id].index;
                if(!__Arr__.nodes[i].init)return;
                __Arr__.nodes[i].init.size = {
                    w:n_w,
                    h:n_h,
                }

                __DB__.nodes[id].init.size = {
                    w:n_w,
                    h:n_h,
                }
                if(el.getAttribute('root') === "true"){
                    let x = $w_e/2 - n_w/2,
                        y = $h_e/2 - n_h/2;
                    el.style.left = `${x}px`;
                    el.style.top  = `${y}px`;

                    __DB__.nodes[id].init.poi = {x,y};
                    __Arr__.nodes[i].init.poi = {x,y};

                    let itp = __tool__.idTrunPoi(id);//获取节点坐标信息
                    __DB__.nodes[id].init.point = itp.point;
                    __Arr__.nodes[i].init.point = itp.point;
                }
            }
            
            __node__.reset();//渲染节点
        }

        // 节点模板
        node(res){
            // 自定义节点样式
            let divClass            = $init.style.node.class?" "+$init.style.node.class:"";
            let hasIcon             = $init.style.node.hasIcon?" xmd-hasIcon":"";
            let nodeIcon            = "";
            let nodeIconStatus      = "";
            let nodeStyleCls        = "";
            let nodeLimit           = $init.nodeLimit?$init.nodeLimit:-1;
            let nodeStyleCls_root   = "";
            let nodeDivClass        = res.nodeDivClass||[];

            let nodeDivClassStr     = '';
            for(let nodeDivCls of nodeDivClass){
                nodeDivClassStr += " "+nodeDivCls;
            }

            if($init.style.node.cls.length > 0){
                $init.style.node.cls.forEach(el=>{
                    if(el.id){
                        divClass = res.id === el.id?divClass + " " + el.name:divClass;
                    }
                    else if(res.clsType === el.type){
                        divClass = divClass + " " + el.name;
                    }
                });
            }

            if(res.styleInit){
                nodeIcon            = res.styleInit.nodeIcon?" "+res.styleInit.nodeIcon:"";
                nodeStyleCls        = res.styleInit.nodeStyleCls?" "+res.styleInit.nodeStyleCls:"";
                nodeStyleCls_root   = res.styleInit.nodeStyleCls_root?" "+res.styleInit.nodeStyleCls_root:"";
                nodeStyleCls        = res.root?nodeStyleCls_root:nodeStyleCls;
            }

            nodeLimit = parseFloat(nodeLimit);
            if(typeof res.styleInit.nodeLimit === "number"&&res.styleInit.nodeLimit>-1){
                nodeLimit = res.styleInit.nodeLimit;
            }
            else{
                if(res.root){
                    nodeLimit = nodeLimit*2;
                }
                else{
                    nodeLimit = nodeLimit;
                }
            }

            if(res.state === 0){
                nodeIconStatus = $init.nodeStateCls.fail;
            }
            else if(res.state === 1){
                nodeIconStatus = $init.nodeStateCls.ing;
            }
            else if(res.state === 2){
                nodeIconStatus = $init.nodeStateCls.success;
            }

            if(res.root)divClass = divClass + " xmd-node-root";

            let _node = document.createElement('div');
            _node.setAttribute('id',res.id);
            _node.setAttribute('pid',res.pid);
            _node.setAttribute('class','xmd-node'+divClass + nodeStyleCls+nodeDivClassStr);

            let _nodeMain = document.createElement('div');
            _nodeMain.setAttribute('class','xmd-node-main zoom' + hasIcon);

            let _nodeIcon = document.createElement('div');
            _nodeIcon.setAttribute('class','xmd-node-icon' + nodeIcon);

            let _nodeLabel = document.createElement('div');
            _nodeLabel.setAttribute('class','xmd-node-txt');
            if(nodeLimit >= 0)_nodeLabel.style.width = nodeLimit + "px";
            let _nodeLabelMain = document.createElement('div');
            _nodeLabelMain.setAttribute('class','xmd-node-txtMain');
            let _nodeLabelSpan = document.createElement('span');
            _nodeLabelSpan.innerHTML = res.label;
            _nodeLabelMain.appendChild(_nodeLabelSpan);

            if(res.root){
                _nodeLabel.setAttribute('class','xmd-node-txt xmd-node-txt111');
                _nodeLabel.innerHTML = res.label;
            }
            else{
                _nodeLabel.appendChild(_nodeLabelMain);
            }
            

            let _nodeStatus = document.createElement('div');
            _nodeStatus.setAttribute('class','xmd-node-status ' + nodeIconStatus);

            let _nodeIndex = document.createElement('div');
            _nodeIndex.setAttribute('class','xmd-nodeIndex');
            let index = parseInt(res.index) + 1;
            if(index < 10){
                index = "0"+index;
            }
            _nodeIndex.innerHTML = index;

            

            // 获取优化级-渲染不同的颜色
            let _priority = res.styleInit.priority;
            let nodePriorityClass = 'xmd-nodePriority-m';
            let nodePriorityTxt = 'M';
            let nodePriorityClassM = 'xmd-nodePriority-mpd';
            if(_priority == 0){
                nodePriorityClass = 'xmd-nodePriority-l';
                nodePriorityTxt = 'L';
                nodePriorityClassM = '';
            }
            else if(_priority == 1){
                nodePriorityClass = 'xmd-nodePriority-m';
                nodePriorityTxt = 'M';
            }
            else if(_priority == 2){
                nodePriorityClass = 'xmd-nodePriority-h';
                nodePriorityTxt = 'H';
                nodePriorityClassM = '';
            }

            let _nodePriority = document.createElement('div');
            _nodePriority.setAttribute('class','xmd-nodePriority ' + nodePriorityClassM);

            let _nodePriorityP = document.createElement('div');
            _nodePriorityP.setAttribute('class','xmd-nodePriority-p ' + nodePriorityClass);
            _nodePriorityP.innerHTML = nodePriorityTxt;
            _nodePriority.appendChild(_nodePriorityP);
            _nodeIcon.appendChild(_nodeIndex);
            _nodeIcon.appendChild(_nodePriority);
            
            _nodeMain.appendChild(_nodeIcon);
            _nodeMain.appendChild(_nodeLabel);
            _nodeMain.appendChild(_nodeStatus);
            
            let _nodeHtml = document.createElement('div');
            _nodeHtml.setAttribute('class','node-html');
            if(res.html && typeof res.html === 'string')_nodeHtml.innerHTML = res.html;

            let _nodeUp = document.createElement('div');
            _nodeUp.setAttribute('class','xmd-node-up');

            let _nodeDown = document.createElement('div');
            _nodeDown.setAttribute('class','xmd-node-down');

            let _nodeIn = document.createElement('div');
            _nodeIn.setAttribute('class','xmd-node-in');

            let _nodeOpt = document.createElement('div');
            _nodeOpt.setAttribute('class','xmd-node-opt');
            _nodeOpt.setAttribute('title',res.label)

            _node.appendChild(_nodeMain);
            _node.appendChild(_nodeHtml);
            _node.appendChild(_nodeIn);
            _node.appendChild(_nodeOpt);
            if(!res.root && res.pid){
                _node.appendChild(_nodeUp);
                _node.appendChild(_nodeDown);
            }
            
            let id = _node.getAttribute('id');
            if(__DB__.nodes[id].root){
                let _nodePullLeft  = document.createElement('div');
                let _nodePullRight = document.createElement('div');
                _nodePullLeft.setAttribute('class','xmd-node-pull-l');
                _nodePullRight.setAttribute('class','xmd-node-pull-r');
                _node.appendChild(_nodePullLeft);
                _node.appendChild(_nodePullRight);
                _node.setAttribute('root',"true");
            }
            else{
                if(__DB__.nodes[id].left){
                    let _nodePullLeft = document.createElement('div');
                    _nodePullLeft.setAttribute('class','xmd-node-pull-l');
                    _node.appendChild(_nodePullLeft);
                }
                else{
                    let _nodePullRight = document.createElement('div');
                    _nodePullRight.setAttribute('class','xmd-node-pull-r');
                    _node.appendChild(_nodePullRight);
                }
            }
            
            
            return _node;
        }

        // 开始无限画布
        auto(){
            $e.onmousedown = (e)=>{
                if(e.button === 2)return;
                //获取鼠标按下的坐标
				let x1 = e.clientX,
                    y1 = e.clientY;
                
                let nowX = e.target.parentNode.offsetLeft,
                    nowY = e.target.parentNode.offsetTop;
                
                let open = $init.hawkEye.open
                // 获取鹰眼当前位置
                let demo_x1,demo_y1;
                if(open){
                    demo_x1 = parseFloat(this.eye.box.style.left)||25;
                    demo_y1 = parseFloat(this.eye.box.style.top)||25;
                }
  
                //给可视区域添加鼠标的移动事件
				document.onmousemove = (ev)=>{
                    document.body.style.cursor='move';
					//获取鼠标移动时的坐标
					let x2 = ev.clientX;
					let y2 = ev.clientY;

					//计算出鼠标的移动距离
					let x = x2 - x1;
					let y = y2 - y1;

					this.x = x;
					this.y = y;

                    // 鹰眼自适应
                    if(open){
                        let eye_x = demo_x1 - (x)/this.eye.scaleW;
                        let eye_y = demo_y1 - (y)/this.eye.scaleH;
                        this.eye.box.style.left = eye_x + "px";
                        this.eye.box.style.top  = eye_y + "px";
                    }

                    $e.style.left = (nowX + x) + "px";
                    $e.style.top  = (nowY + y) + "px";
				};

				document.onmouseup = (ev)=>{
					document.onmousemove = null;
					document.onmouseup   = null;
                    document.body.style.cursor='default';
				};
            }

            $e.onmousewheel = (e)=>{
                let max = $init.scale.max,
                    min = $init.scale.min;
				if(e.wheelDelta > 0){
					if($scale > max)return;
					$scale = parseFloat($scale) + 0.1;
					$scale = $scale.toFixed(1);
				}
				else{
					if($scale < min)return;
					$scale = parseFloat($scale) - 0.1;
					$scale = $scale.toFixed(1);
				}
                
                window.dispatchEvent($myEvent.scaleChange);
                $scaleOut = $scale;
                $e.style.transform = `scale(${$scale})`;
			}	
        }

        // 操作画布 
        option(){
            $e.oncontextmenu = (e)=>{
                e.preventDefault();
                e.stopPropagation();

                $opt.cntMenu =  __tool__.deepCopy();
                window.dispatchEvent($myEvent.cntMenu);     // 右键节点
                
                __cntMenu__.use({
                    type:'canvas',
                    x:e.clientX,
                    y:e.clientY,
                });
            }
        }

        // 鹰眼
        hawkEye(){
            let open = $init.hawkEye.open,
                pid  = $init.hawkEye.pid;
            
            fn_remove.call(this);
            if(open)fn_create.call(this);

            function fn_remove(){
                let el = document.getElementById('eye');
                if(el)el.remove();
                
                if(this.eye.box){
                    this.eye.box.style.left = "25px";
                    this.eye.box.style.top  = "25px";
                    this.x = null;
                    this.y = null;

                    // if(this.canvas_x){
                    //     $e.style.left = this.canvas_x + "px";
                    //     $e.style.top  = this.canvas_y + "px";
                    // }
                }
            }

            function fn_create(){
                let canvas = document.querySelector("#canvas");
                let canvas_w = canvas.getBoundingClientRect().width;
                let canvas_h = canvas.getBoundingClientRect().height;

                let el_eye_box = document.createElement('div');
                el_eye_box.setAttribute('id','eyeBox');
                el_eye_box.setAttribute('class','eyeBoxCls');

                let el_eye = document.createElement('div');
                el_eye.setAttribute('id','eye');
                el_eye.setAttribute('class','eyeCls');
                el_eye.innerText = "";
                el_eye.appendChild(el_eye_box);
                let el_s = $s;
                if(pid){
                    el_s = document.getElementById(pid);
                    if(el_s){
                        let w = el_s.getBoundingClientRect().width;
                        let h = el_s.getBoundingClientRect().height + 10;
                        let boxLeft = el_s.offsetLeft - 75;
                        el_eye.style.left = boxLeft + "px";
                        el_eye.style.top = (el_s.offsetTop + h) + "px";
                    }
                };
                el_s = el_s||$s;
                document.body.appendChild(el_eye);

                let el_eye_box_w = el_eye_box.getBoundingClientRect().width;
                el_eye_box_w = el_eye_box_w*$init.canvasScale;
                let el_eye_box_h = el_eye_box.getBoundingClientRect().height;
                el_eye_box_h = el_eye_box_h*$init.canvasScale;
                let eyeScaleW = canvas_w/el_eye_box_w;
                let eyeScaleH = canvas_h/el_eye_box_h;
                this.eye.scaleW = eyeScaleW;
                this.eye.scaleH = eyeScaleH;
                this.eye.box = el_eye_box;
                this.eye.el = el_eye;

                let canvas_x = parseFloat($e.style.left);
                let canvas_y = parseFloat($e.style.top);

                if(this.canvas_x){
                    $e.style.left = this.canvas_x + "px";
                    $e.style.top  = this.canvas_y + "px";
                }

                let demo111_x1 = parseFloat(this.eye.box.style.left)||25;
                let demo111_y1 = parseFloat(this.eye.box.style.top)||25;
                let eye_x = demo111_x1 - (this.x)/this.eye.scaleW;
                let eye_y = demo111_y1 - (this.y)/this.eye.scaleH;
                this.eye.box.style.left = eye_x + "px";
                this.eye.box.style.top  = eye_y + "px";

                el_eye.ondblclick = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();
                    $e.style.left = this.canvas_x + "px";
                    $e.style.top  = this.canvas_y + "px";
                    el_eye_box.style.left = "25px";
                    el_eye_box.style.top  = "25px";
                    this.x = null;
                    this.y = null;
                }

                el_eye_box.onmousedown = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();
                    let l  = e.target.offsetLeft,
                        t  = e.target.offsetTop,
                        x1 = e.clientX,
                        y1 = e.clientY;
                    
                    let demo_x1 = parseFloat($e.style.left);
                    let demo_y1 = parseFloat($e.style.top);

                    document.onmousemove = (ev)=>{
                        let x2 = ev.clientX;
                        let y2 = ev.clientY;
                        let eye_x = l + x2 - x1;
                        let eye_y = t + y2 - y1;
                        el_eye_box.style.left = eye_x + "px";
                        el_eye_box.style.top  = eye_y + "px";

                        $e.style.left = (demo_x1 - (x2 - x1)*eyeScaleW) + "px";
                        $e.style.top  = (demo_y1 - (y2 - y1)*eyeScaleH) + "px";
                    }

                    document.onmouseup = (ev)=>{
                        document.onmousemove = null;
                        document.onmouseup   = null;
                    }
                }
            }

            
        }

        // 清除画布内容
        clear(){
            let svgAllmain = document.getElementById('svgAllmain');
            svgAllmain.innerHTML = "";
            $e.innerHTML = "";
            $e.appendChild(svgAllmain)
        }
    }

    // 操作模块
    class xOpt {
        constructor(){}
        pull(){
            
            let el_ls = document.querySelectorAll('.xmd-node-pull-l');
            let el_rs = document.querySelectorAll('.xmd-node-pull-r');
            if(el_rs.length > 0){
                el_rs.forEach(el=>{
                    el.onclick = (e)=>{
                        e.stopPropagation();
                        e.preventDefault();
                        ck.call(this,{el,e});
                    }
                })
            }

            if(el_ls.length > 0){
                el_ls.forEach(el=>{
                    el.onclick = (e)=>{
                        e.stopPropagation();
                        e.preventDefault();
                        ck.call(this,{el,e});
                    }
                })
            }

            function ck(res) {
                let el = res.el;
                let e = res.e;
                let id = el.parentNode.getAttribute('id');

                let is = el.classList;
                let newCls = is[1] === "xmd-node-pull-remove"?"xmd-node-pull-plus":"xmd-node-pull-remove";
                el.setAttribute('class',is[0] + " " + newCls);

                let plus = null;
                let the = __DB__.nodes[id];
                if(the.root){
                    if(is[0] === "xmd-node-pull-l"){
                        the.init.pull.l.plus = !the.init.pull.l.plus;
                        // the.init.pull.l.open = !the.init.pull.l.open;
                        __Arr__.nodes[the.index].init.pull.l.plus = the.init.pull.l.plus;
                        // __Arr__.nodes[the.index].init.pull.l.open = the.init.pull.l.open;
                    }
                    else if(is[0] === "xmd-node-pull-r"){
                        the.init.pull.r.plus = !the.init.pull.r.plus;
                        // the.init.pull.r.open = !the.init.pull.r.open;
                        __Arr__.nodes[the.index].init.pull.r.plus = the.init.pull.r.plus;
                        // __Arr__.nodes[the.index].init.pull.r.open = the.init.pull.r.open;
                    }
                }
                else{
                    if(the.left){
                        the.init.pull.l.plus = !the.init.pull.l.plus;
                        // the.init.pull.l.open = !the.init.pull.l.open;
                        __Arr__.nodes[the.index].init.pull.l.plus = the.init.pull.l.plus;
                        // __Arr__.nodes[the.index].init.pull.l.open = the.init.pull.l.open;
                    }
                    else{
                        the.init.pull.r.plus = !the.init.pull.r.plus;
                        the.init.pull.r.open = !the.init.pull.r.open;
                        __Arr__.nodes[the.index].init.pull.r.plus = the.init.pull.r.plus;
                        // __Arr__.nodes[the.index].init.pull.r.open = the.init.pull.r.open;
                    }
                }
                
                __canvas__.load({item:__Arr__.nodes,type:"noinit"});
            }
        }
    }

    // 线模块
    class xLine {
        constructor(){
            this.colors = $init.style.contactLine.colors;
        }
        add(res,tt){
            let elSVG = document.getElementById('svg');

            let x1 = res.x1,
                x2 = res.x2,
                y1 = res.y1,
                y2 = res.y2;
            
            let id = res.id;

            let color = this.colors[0];
            let contactLineSize = $init.style.contactLine.size;
            let line = `
                <line 
                    id="${id}" 
                    x1="${x1}" 
                    y1="${y1}" 
                    x2="${x2}" 
                    y2="${y2}" 
                    style="stroke:${color};stroke-width:${contactLineSize};z-index:10;pointer-events:visiblePainted" 
                    />
            `;
            
            elSVG.innerHTML += line;
        }

        // 删除树结构线
        del(res){
            if(!res || Object.keys(res).length === 0)return;
            let id = res.id;
            if(!id)return;
            let elSVG = document.getElementById('svg');
            let elLine = document.getElementById(id);
            if(elLine)elSVG.removeChild(elLine);
        }

        // 新增关联线
        add_contact(res){
            let elSVG = document.getElementById('svg_contact');

            if(res.id === 'temporaryLine'){
                elSVG = document.getElementById('svgTemporary');
            }

            let x1 = res.x1,
                x2 = res.x2,
                y1 = res.y1,
                y2 = res.y2;
            
            let id = res.id;

            let color = this.colors[0];
            let contactLineSize = $init.style.contactLine.size;
            let line = `
                <line 
                    id="${id}" 
                    x1="${x1}" 
                    y1="${y1}" 
                    x2="${x2}" 
                    y2="${y2}" 
                    style="stroke:${color};stroke-width:${contactLineSize};z-index:10;pointer-events:visiblePainted" 
                    marker-end='url(#${id}_marker111)'
                    />
                <defs>
					<marker style="overflow:visible" id='${id}_marker111' markerWidth='12' markerHeight='12' refx='0' refy='0' orient='auto' viewBox="0 -15 30 30">
						<path d='M-10,-5L0,0L-10,5' style='fill:${color}' id='${id}_marker'  />
					</marker>
				</defs>
            `;
            
            if(res.id === 'temporaryLine'){
                elSVG.innerHTML = line;
            }
            else{
                elSVG.innerHTML += line;
            }
            
        }

        // 删除关联线
        del_contact(res){
            if(!res || Object.keys(res).length === 0)return;
            let id = res.id;
            if(!id)return;
            let elSVG = document.getElementById('svg_contact');
            let elLine = document.getElementById(id);
            if(elLine)elSVG.removeChild(elLine);
        }

        // 更新关联线
        update_contact(res){
            let line = document.getElementById(res.id);
            line.setAttribute('x2',res.x);
            line.setAttribute('y2',res.y);
        }

        set(res){
            let id = res.id;
            let el = document.getElementById(id);
            if(!el)return;
            el.setAttribute('x1',res.x1);
            el.setAttribute('y1',res.y1);
            el.setAttribute('x2',res.x2);
            el.setAttribute('y2',res.y2);
        }

        evt(res){
            // __Arr__.contactLinesSVG.forEach(el=>{
            //     let ele = document.getElementById(el.id);
            //     let aaaa = el.id + "_marker";
            //     let ele_marker = document.getElementById(aaaa);
            //     ele.onclick = (e)=>{
            //         ele.style.stroke = "#f00";
            //         ele_marker.style.fill = "#f00";
            //     }
            // })
        }

        clear(){
            let elSVG = document.getElementById('svg_contact');
            if(elSVG)elSVG.innerHTML = "";
        }

        show_contact(){
            this.clear();
            __Arr__.contactLinesSVG = [];
            let lines = __Arr__.contactLines;

            lines.forEach(el=>{
                let id_s    = el.s,
                    id_e    = el.e,
                    type    = el.type;
                
                let s       = __DB__.nodes[id_s],
                    e       = __DB__.nodes[id_e];

                let s_mid   = s.init.point.mid;
                let e_mid   = e.init.point.mid;
                let x1      = s_mid[0];
                let y1      = s_mid[1];
                let x2      = e_mid[0];
                let y2      = e_mid[1];

                let newLine = {
                    id:"contantLine"+(new Date()).getTime(),
                    x1,y1,x2,y2
                };
                this.add_contact(newLine);
                __Arr__.contactLinesSVG.push(newLine);
            })

            this.evt();
        }

        show(){
            this.clear();
            let lines = __Arr__.contactLines;
            setTimeout(()=>{
                lines.forEach(el=>{
                    let id_s = el.id_s;
                    let id_e = el.id_e;
                    let s = __DB__.nodes[id_s];
                    let e = __DB__.nodes[id_e];

                    if(!s || !s.show)return;
                    if(!e || !e.show)return;

                    let s_mid = s.init.point.mid;
                    let e_mid = e.init.point.mid;
                    let x1 = s_mid[0];
                    let y1 = s_mid[1];
                    let x2 = e_mid[0];
                    let y2 = e_mid[1];

                    let newLine = {
                        // id:'line_'+(new Date()).getTime(),
                        id:el.id+(new Date()).getTime(),
                        x1,y1,x2,y2
                    };
                    this.add_contact(newLine);

                    let lineEl = document.getElementById(newLine.id);
                    lineEl.oncontextmenu = (e)=>{
                        e.preventDefault();
                        e.stopPropagation();
                        __cntMenu__.use({
                            id  :newLine.id,
                            type:'contactLine',
                            x   :e.clientX,
                            y   :e.clientY,
                        });
                    }
                });
            })
        }

        draw(res){
            let e  = res.e,
                el = res.el,
                parentNode = el.parentNode;
            let nodeId = parentNode.getAttribute('id');
            let node = __DB__.nodes[nodeId];

            this.add_contact({
                id:"temporaryLine",
                x1:node.init.point.mid[0],
                y1:node.init.point.mid[1],
                x2:node.init.point.mid[0],
                y2:node.init.point.mid[1],
            })

            let temporaryLineEl = document.getElementById('temporaryLine');
            temporaryLineEl.style.pointerEvents = 'none';

            document.onmousemove = (ev)=>{
                this.update_contact({
                    id:"temporaryLine",
                    x:(ev.clientX - e.clientX) + node.init.point.a[0] + e.offsetX,
                    y:(ev.clientY - e.clientY) + node.init.point.a[1] + e.offsetY
                })
            }

            document.onmouseup = (e)=>{
                document.onmousemove = null;
                document.onmouseup   = null;

                let id = e.target.parentNode.getAttribute("id");
                let nodeObj = __DB__.nodes[id];

                if(!nodeObj){
                    document.getElementById('svgTemporary').removeChild(temporaryLineEl);
                    return;
                }

                this.update_contact({
                    id:"temporaryLine",
                    x:nodeObj.init.point.mid[0],
                    y:nodeObj.init.point.mid[1],
                });

                let obj = {
                    s:nodeId,
                    e:id
                };
                let contactLines = __Arr__.contactLines;

                let sign = true;
                for(let el of contactLines){
                    if(el.s === obj.s && el.e === obj.e){
                        sign = false;
                        break;
                    }
                    else if(el.s === obj.e && el.e === obj.s){
                        sign = false;
                        break;
                    }
                }

                if(sign){
                    contactLines.push({
                        s:obj.s,
                        e:obj.e,
                        type:"solid",
                    });

                    __Arr__.temporaryLine = {s:obj.s,e:obj.e};
                    
                }
                document.getElementById('svgTemporary').removeChild(temporaryLineEl);
                temporaryLineEl.style.pointerEvents = 'auto';

                window.dispatchEvent($myEvent.temporaryLineUp);

                __line__.show_contact();

            }
        }
    }

    // 节点模块
    class xNode {
        constructor(){}
        add(res){
            __format__.nodeInit({
                label   : res.label,
                id      : res.id, 
                pid     : res.pid,
                left    : res.left,
                html    : res.html||null,
                type    : res.type||"rule",
                styleInit:res.styleInit||null,
                isInitial:"no",
            });

            __canvas__.load();
        }

        sel(){}

        del(res){
            let id = res.id;
            for(let i in __Arr__.nodes){
                let el = __Arr__.nodes[i];
                if(el.id === id){
                    __Arr__.nodes.splice(i,1);
                    delete __DB__.nodes[id];
                    continue;
                }
            }
            
            let ids = __tool__.deepCopy($nodesMany_ids[id]);
            __canvas__.load({items:__Arr__.nodes});

            for(let el of ids){
                let i = __DB__.nodes[el].index;
                __Arr__.nodes.splice(i,1);
                delete __DB__.nodes[el];
            }

            __Arr__.nodes.forEach((el,i)=>{
                el.index = i;
                if(!__DB__.nodes[el.id])return;
                __DB__.nodes[el.id].index = i;
            });
        }

        update(){}

        // 节点重置
        reset(){
            __format__.otm();// 一维变多维

            this.init_node();// 配置树节点
            this.init_free();// 配置自由节点

            __format__.node_show();// 配置节点是否显示

            __format__.line();
            __draw__.use();// 节点的拖拽初始化

            
        }

        // 配置树节点
        init_node(){
            __format__.node_maxw($nodesMany);
            __format__.node_maxh($nodesMany);
            __format__.node_poi($nodesMany);
            __format__.node_pull($nodesMany);

            __Arr__.nodes.forEach(el=>{
                let id  = el.id;
                let itp = __tool__.idTrunPoi(id);
                __DB__.nodes[id].init.point = itp.point;
                el.init.point = itp.point;
            })
        }

        // 配置自由节点
        init_free(){
            if($nodesMany_frees.length === 0)return;

            for(let el of $nodesMany_frees){
                let arr = [];
                arr.push(el)
                __format__.node_maxw(arr);
                __format__.node_maxh(arr);
                __format__.node_poi(arr);
                __format__.node_pull(arr);
            }
            
        }

        // 事件管理
        evt(){
            const nodes = document.querySelectorAll(".xmd-node")
            for(let el of nodes){
                el.onmousedown = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();
                }

                el.oncontextmenu = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();
                }
            }

            const nodeOpts = document.querySelectorAll(".xmd-node-opt");
            for(let i=0;i<nodeOpts.length;i++){
                let el = nodeOpts[i];
                let parentNode = el.parentNode;
                let id = parentNode.getAttribute('id');

                el.onmousedown = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();

                    if(!$isDraw)return;
                    
                    if($opt.line){
                        fn_line(e,el);
                    }
                    else{
                        fn_drag(e,el);
                    }

                }

                el.onclick = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();

                    __cntMenu__.clear();

                    for(let el of nodes){
                        let cls_el = el.getAttribute('class');
                        if(cls_el.indexOf('active') > -1){
                            el.classList.remove('xmd-node-active');
                        }
                    }
                    parentNode.classList.add("xmd-node-active");

                    // +-按钮事件穿透
                    let pull_r = document.querySelectorAll('.xmd-node-pull-r');
                    let pull_l = document.querySelectorAll('.xmd-node-pull-l');
                    // +-按钮事件穿透
                    pull_r.forEach(el=>{
                        el.style.pointerEvents = "auto";
                    });

                    pull_l.forEach(el=>{
                        el.style.pointerEvents = "auto";
                    });

                    if(automatic.isDraw){
                        automatic.endName = id;
                        // automatic.contect({
                        //     nodes   : __Arr__.nodes,
                        //     ids     : __DB__.nodes,
                        //     canvas  : document.getElementById('automaticConnection')
                        // })

                        $opt.contactLineEvt =  {
                            startId     : automatic.startName,
                            endId       : automatic.endName,
                        };
                        window.dispatchEvent($myEvent.contactLineEvt);     // 关联线连接成功
                    }
                    
                    $opt.nodeClick =  __tool__.deepCopy(__DB__.nodes[id]);
                    window.dispatchEvent($myEvent.nodeClick);                   // 单击节点
                }

                el.oncontextmenu = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();

                    $opt.nodeCntMenu =  __tool__.deepCopy(__DB__.nodes[id]);
                    window.dispatchEvent($myEvent.nodeCntMenu);     // 右键节点
                    $opt.cntMenu =  __tool__.deepCopy();
                    window.dispatchEvent($myEvent.cntMenu);     // 右键节点
                    
                    __cntMenu__.use({
                        x:e.clientX,
                        y:e.clientY,
                        id,
                    });
                }

                el.ondblclick = (e)=>{
                    e.stopPropagation();
                    e.preventDefault();
                    $opt.nodeDblclick =  __tool__.deepCopy(__DB__.nodes[id]);
                    window.dispatchEvent($myEvent.nodeDblclick);     // 双击节点

                    // +-按钮事件穿透
                    let pull_r = document.querySelectorAll('.xmd-node-pull-r');
                    let pull_l = document.querySelectorAll('.xmd-node-pull-l');
                    // +-按钮事件穿透
                    pull_r.forEach(el=>{
                        el.style.pointerEvents = "auto";
                    });

                    pull_l.forEach(el=>{
                        el.style.pointerEvents = "auto";
                    });

                }
            }

            function fn_drag(e,el){
                let cntEl = document.getElementById('cntMenu');
                if(cntEl)return;
                __draw__.nodeMouseDown({el:el.parentNode,e});// 可拖拽节点点击事件
            }
            function fn_line(e,el){
                __line__.draw({e,el});
            }
        }
    }

    // 数据解析模块
    class xFormate {
        constructor(){this.num=0}
        // 一维变多维
        otm(){
            let nodes = __Arr__.nodes;

            //解析过滤出自由节点
            __Arr__.frees = [];
            nodes.forEach((el,i)=>{
                // __DB__.nodes[el.id].nodeType = __DB__.nodes[el.id].nodeType==='free'?'node':__DB__.nodes[el.id].nodeType;
                if(el.pid !== null || el.root)return;
                el.nodeType = "free";
                el.pid = null;
                __DB__.nodes[el.id].nodeType = 'free';
                let itemEl = __tool__.deepCopy(el);

                __Arr__.frees.push(itemEl);

                $nEls[el.index].style.top  = el.init.poi.y + "px";
                $nEls[el.index].style.left = el.init.poi.x + "px";
            })
            $nodesMany          = [];
            $nodesMany_frees    = [];
            let nodesMany = nodes.map((el,i)=>{
                let hasRootLeft = false;
                let childrenLeft = nodes.filter(item => {
                    let sign = item.pid === el.id && item.left && !el.init.pull.l.plus;
                    if(!hasRootLeft){
                        hasRootLeft = item.pid === el.id && item.left;
                    }
                    // el.init.pull.l.has = item.pid === el.id && item.left;
                    return sign;
                });

                let hasRootRight = false;
                let childrenRight = nodes.filter(item => {
                    let sign = item.pid === el.id && !item.left && !el.init.pull.r.plus;
                    if(!hasRootRight){
                        hasRootRight = item.pid === el.id && !item.left;
                    }
                    // el.init.pull.r.has = item.pid === el.id && !item.left;
                    return sign;
                });

                let hasLeft = false;
                let hasRight = false;
                let children = nodes.filter(item=>{
                    if(item.pid === el.id){
                        if(!el.root){
                            if(!el.root)item.left = el.left;
                        }
                    }
                    let plus = el.left?!el.init.pull.l.plus:!el.init.pull.r.plus;

                    if(el.left){
                        if(!hasLeft)hasLeft = item.pid === el.id;
                    }
                    else{
                        if(!hasRight)hasRight = item.pid === el.id;
                    }
                    
                    return item.pid === el.id && plus;
                });

                if(el.root){
                    el.init.pull.l.has = hasRootLeft;
                    el.init.pull.r.has = hasRootRight;
                    el.childrenRoot = {
                        left :childrenLeft,
                        right:childrenRight,
                    }
                }
                else{
                    if(el.nodeType === "node"){el.show = false;}
                    el.children = children;

                    if(el.left){
                        el.init.pull.l.has = hasLeft;
                    }
                    else{
                        el.init.pull.r.has = hasRight;
                    }
                }

                return el;
            });

            // 获取自由节点集
            $nodesMany_frees = nodesMany.filter(item=>{
                return item.nodeType === "free";
            });

            $nodesMany = nodesMany.filter(item=>{
                return item.root;
            });

            // 生成节点的子节点列表
            let charr = {};
            selNodeChildren($nodesMany);// 树节点
            selNodeChildren($nodesMany_frees);// 自由节点
            $nodesMany_ids = charr;// 节点的子节点ID列表
            
            // 生成节点的子节点ID列表
            function selNodeChildren(items){
                let total = []
                items.forEach((el,i)=>{
                    if(el.root){
                        let oa = selNodeChildren(el.childrenRoot.left);
                        let ob = selNodeChildren(el.childrenRoot.right);
                        oa.forEach(ee=>{
                            total.push(ee)
                        })
                        ob.forEach(ee=>{
                            total.push(ee)
                        })

                        charr[el.id] = total;
                    }
                    else{
                        let oc = selNodeChildren(el.children);
                        
                        oc.forEach(ee=>{
                            total.push(ee)
                        })
                        total.push(el.id);
                        charr[el.id] = oc;

                    }
                })

                return total;
            }
        }

        // 节点数据初始化
        nodeInit(el,i){
            let obj = {
                id                  :el.id,
                pid                 :el.pid,
                root                :el.root||false,
                label               :el.label,
                type                :el.type||"rule",
                nodeType            :el.nodeType==="free"?"free":"node",
                data                :el.data||null,
                left                :el.left||false,
                html                :el.html||null,
                has                 :true,//判断当前节点是否还存在，如不存在会在下一次渲染时自动删除
                show                :true,//是否显示
                clsType             :el.clsType||null,
                index               :i,
                indexMany           :[],
                children            :[],
                childrenRoot        :{
                    left    :[],
                    right   :[],
                },
                nodeIcon            :el.nodeIcon||"",           // 节点图标
                nodeIconStatus      :el.nodeIconStatus||"",     // 节点图标-状态
                nodeStyleCls        :el.nodeStyleCls||"",       // 节点样式
                nodeDivClass        :el.nodeDivClass||false,    // 节点自定义样式
                isInitial           :el.isInitial||"yes",       // 判断是否要重置
                styleInit           :{
                    nodeIcon            :(el.styleInit&&el.styleInit.nodeIcon)||"",           // 节点图标
                    nodeIconStatus      :(el.styleInit&&el.styleInit.nodeIconStatus)||"",     // 节点图标-状态
                    nodeStyleCls        :(el.styleInit&&el.styleInit.nodeStyleCls)||"",       // 节点样式
                    nodeStyleCls_root   :(el.styleInit&&el.styleInit.nodeStyleCls_root)||"",  // 节点样式-根节点
                    priority            :(el.styleInit&&el.styleInit.priority>-1)?el.styleInit.priority:-1,
                    nodeLimit           :(el.styleInit&&el.styleInit.nodeLimit)||-1,
                },
                init                :{
                    show    :false,
                    poi     :el.init?el.init.poi:{x:0,y:0},
                    point   :{a:[],b:[],c:[],d:[],ab:[],bc:[],cd:[],ad:[],mid:[]},
                    size    :{w:0,h:0},
                    max     :{top:0,h:0,w:0,ch:0},//h是包括节点自己的最大高，ch是除了自己所有子节点的最大高
                    maxRoot :{
                        l:{top:0,h:0,w:0,ch:0},//h是包括节点自己的最大高，ch是除了自己所有子节点的最大高
                        r:{top:0,h:0,w:0,ch:0},//h是包括节点自己的最大高，ch是除了自己所有子节点的最大高
                    },
                    pull    :{
                        l:{
                            show:false,
                            plus:false,
                            has:false,
                        },
                        r:{
                            show:false,
                            plus:false,
                            has:false,
                        }
                    },
                }
            };

            if(el.isInitial === "no" && el.init && el.init.pull){
                obj.init.pull = el.init.pull;


                if(el.left){
                    if(!el.init.pull.l.has){
                        el.init.pull.l.plus = false;
                    }
                }
                else{
                    if(!el.init.pull.r.has){
                        el.init.pull.r.plus = false;
                    }
                }
            }
            else{
                obj.init.pull.l.plus = $init.pull;
                obj.init.pull.r.plus = $init.pull;
            }

            obj.isInitial = 'no';

            if(el.root){
                delete obj.children;
                delete obj.init.max;
            }
            else{
                delete obj.childrenRoot;
                delete obj.init.maxRoot;
            }

            __Arr__.nodes.push(obj);
            __DB__.nodes[el.id] = obj;
            $e.appendChild(__canvas__.node(el));
        }

        // 对外可被调用
        node(){
            this.otm();// 一维变多维
            this.node_maxw($nodesMany);
            this.node_maxh($nodesMany);
            this.node_poi($nodesMany);
            this.node_pull($nodesMany);

            __Arr__.nodes.forEach(el=>{
                let id  = el.id;
                let itp = __tool__.idTrunPoi(id);
                __DB__.nodes[id].init.point = itp.point;
                el.init.point = itp.point;
            })

            

            
            if($nodesMany_frees.length > 0){
                let arr = [];
                arr.push($nodesMany_frees[0])
                this.node_maxw(arr);
                this.node_maxh(arr);
                this.node_poi(arr);
                this.node_pull(arr);
            }

            this.node_show();

            this.line();
            __draw__.use();// 节点的拖拽初始化
            
            
        }

        // 解析节点 计算出节点的最大高
        node_maxh(items){
            recursion(items,null);
            return items;
            function recursion(items,parent){
                let max = {
                    l:{w:0,h:0},
                    r:{w:0,h:0},
                }
                items.forEach((el,i)=>{
                    if(el.root){
                        let rb = null,h = 0,w = 0,maxh = 0,maxw = 0;
                        if(el.childrenRoot.left.length > 0){
                            rb = recursion(el.childrenRoot.left,el);
                            // 最大高
                            maxh = rb.l.h;
                            el.init.maxRoot.l.ch = maxh;
                            h = el.init.size.h;
                            maxh = h<maxh?maxh:h;
                            el.init.maxRoot.l.h = maxh;

                            let mid = el.init.point.mid;
                            el.init.maxRoot.l.top = mid[1] - maxh/2;

                            __Arr__.nodes[0].init.maxRoot.l.top = el.init.maxRoot.l.top;
                            __Arr__.nodes[0].init.maxRoot.l.h   = maxh;
                        }

                        if(el.childrenRoot.right.length > 0){
                            let rb = recursion(el.childrenRoot.right,el);
                            // 最大高
                            maxh = rb.r.h;
                            el.init.maxRoot.r.ch = maxh;
                            h = el.init.size.h;
                            maxh = h<maxh?maxh:h;
                            el.init.maxRoot.r.h = maxh;

                            let mid = el.init.point.mid;
                            el.init.maxRoot.r.top = mid[1] - maxh/2;

                            __Arr__.nodes[0].init.maxRoot.r.top = el.init.maxRoot.r.top;
                            __Arr__.nodes[0].init.maxRoot.r.h = maxh;
                        }

                        return;
                    }

                    el.show = true;
                    if(parent && !parent.root)el.left = parent.left;

                    if(el.left){
                        fn_left(el);
                    }
                    else{
                        fn_right(el);
                    }
                })

                return max;
                
                function fn_left(item) {
                    let h = parseFloat(item.init.size.h.toFixed(2)) + $init.style.node.space_y;
                    // 如果没有同胞就减掉垂直间距
                    if(parent&&parent.root&&parent.childrenRoot.left.length === 1){
                        h = h - $init.style.node.space_y;
                    }
                    else if(parent&&parent.children && parent.children.length === 1){
                        h = h - $init.style.node.space_y;
                    }

                    if(item.children.length > 0){
                        let w1 = item.children[0].init.size.w + $init.style.node.space_x;
                        item.children.forEach(el=>{
                            let w = el.init.size.w + $init.style.node.space_x;
                            w1 = w1>w?w1:w;
                        });

                        max.l.w += w1;

                        let rb = recursion(item.children,item);
                        let maxh = rb.l.h;
                        item.init.max.ch = maxh;
                        maxh = h<maxh?maxh:h;
                        item.init.max.h = maxh;
                        max.l.h += maxh;
                        max.l.w += rb.l.w;
                        return;
                    }

                    item.init.max.h = h;
                    item.init.max.ch = h;
                    max.l.h += h;
                };
    
                function fn_right(item) {
                    let h = parseFloat(item.init.size.h.toFixed(2)) + $init.style.node.space_y;
                    // 如果没有同胞就减掉垂直间距
                    if(parent&&parent.root&&parent.childrenRoot.right.length === 1){
                        h = h - $init.style.node.space_y;
                    }
                    else if(parent&&parent.children && parent.children.length === 1){
                        h = h - $init.style.node.space_y;
                    }

                    if(item.children.length > 0){
                        let w1 = item.children[0].init.size.w + $init.style.node.space_x;
                        item.children.forEach(el=>{
                            let w = el.init.size.w + $init.style.node.space_x;
                            w1 = w1>w?w1:w;
                        });
                        max.r.w += w1;

                        let rb = recursion(item.children,item);
                        let maxh = rb.r.h;
                        item.init.max.ch = maxh;

                        maxh = h<maxh?maxh:h;
                        item.init.max.h = maxh;
                        max.r.h += maxh;
                        return;
                    }

                    
                    
                    item.init.max.h = h;
                    item.init.max.ch = h;
                    max.r.h += h;
                };
            }
        }

        // 解析节点 计算出节点的最大宽
        node_maxw(items){
            recursion(items);
            function recursion(items) {
                let maxw = 0;
                items.forEach(el=>{
                    if(el.root){
                        if(el.childrenRoot.left.length > 0){
                            recursion(el.childrenRoot.left);

                            let w1 = el.childrenRoot.left[0].init.max.w;
                            el.childrenRoot.left.forEach(item=>{
                                let w = item.init.max.w;
                                w1 = w1>w?w1:w;
                            });

                            el.init.maxRoot.l.w = w1;
                            __Arr__.nodes[0].init.maxRoot.l.w = w1;
                        }
                        if(el.childrenRoot.right.length > 0){
                            recursion(el.childrenRoot.right);

                            let w1 = el.childrenRoot.right[0].init.max.w;
                            el.childrenRoot.right.forEach(item=>{
                                let w = item.init.max.w;
                                w1 = w1>w?w1:w;
                            });

                            el.init.maxRoot.r.w = w1;
                            __Arr__.nodes[0].init.maxRoot.r.w = w1;
                        }
                    }
                    else{
                        el.show = true;
                        fn_child(el);
                    }
                })

                return maxw;

                function fn_child(item) {
                    let w = item.init.size.w + $init.style.node.space_x;
                    w = w/$scale;
                    item.init.max.w = w;
                    if(item.children.length > 0){
                        let rb = recursion(item.children);
                        let w1 = item.children[0].init.max.w;
                        item.children.forEach(el=>{
                            let w = el.init.max.w;
                            w1 = w1>w?w1:w;
                        });
                        maxw = w1 + w;
                        item.init.max.w = rb + w;
                    }
                    else{
                        maxw = maxw>w?maxw:w;
                    }
                }
            }
        }

        // 解析节点 计算出需要的坐标
        node_poi(items){
            $nEls = document.querySelectorAll('.xmd-node');
            recursion(items);
            return items;
            function recursion(items,parent){
                let myChild = [];
                items.forEach((el,i)=>{
                    if(el.root){
                        if(el.childrenRoot.left.length > 0){
                            myChild.push({children:el.childrenRoot.left,parent:el,root:true});
                        }

                        if(el.childrenRoot.right.length > 0){
                            myChild.push({children:el.childrenRoot.right,parent:el,root:true});
                        }
                        return;
                    }

                    myChild.push({
                        children:el.children,
                        parent:el,
                        left:el.left,
                    });

                    if(el.left){
                        fn_left(el,i);
                    }
                    else{
                        fn_right(el,i);
                    }
                });

                if(myChild.length === 0)return;

                for(let el of myChild){
                    recursion(el.children,el.parent);
                }

                function fn_left(item,index) {
                    if(!parent)return;
                    let pMid = parent.init.point.mid;
                    let pCh = parent.root?parent.init.maxRoot.l.ch:parent.init.max.ch;
                    let ph = parent.root?parent.init.maxRoot.l.h:parent.init.max.h;
                    let left = pMid[0] - parent.init.size.w/2 - $init.style.node.space_x - item.init.size.w;
                    let maxTop = pMid[1] - ph/2;

                    if(index === 0){
                        let oo = item.init.max.h/2 - item.init.size.h/2;//如果自己小于后代的高度，减掉除自身外的高度除2来做为本节点的TOP
                        if(ph > pCh){maxTop = pMid[1] - pCh/2;}// 如果自己比后代的高度要高度，改变最大TOP
                        
                        let top  = maxTop + oo;

                        item.init.max.top   = maxTop;
                        item.init.poi.x     = left;
                        item.init.poi.y     = top;
                        $nEls[item.index].style.left = left + "px";
                        $nEls[item.index].style.top  = top  + "px";
                    }
                    else{
                        let top = items[index-1].init.max.top + items[index-1].init.max.h;
                        item.init.max.top = top;
                        top = top + (item.init.max.h/2 - item.init.size.h/2);
                        item.init.poi.x = left;
                        item.init.poi.y = top;
                        $nEls[item.index].style.left = left + "px";
                        $nEls[item.index].style.top  = top + "px";
                    }
                    
                    
                    let itemPoi = __tool__.idTrunPoi(item.id);
                    item.init.point = itemPoi.point;
                };
    
                function fn_right(item,index) {
                    if(!parent)return;
                    let pMid = parent.init.point.mid;
                    let pCh = parent.root?parent.init.maxRoot.r.ch:parent.init.max.ch;
                    let ph = parent.root?parent.init.maxRoot.r.h:parent.init.max.h;
                    let left = pMid[0] + parent.init.size.w/2 + $init.style.node.space_x;
                    let maxTop = pMid[1] - ph/2;

                    if(index === 0){
                        let oo = item.init.max.h/2 - item.init.size.h/2;//如果自己小于后代的高度，减掉除自身外的高度除2来做为本节点的TOP
                        if(ph > pCh){maxTop = pMid[1] - pCh/2;}// 如果自己比后代的高度要高度，改变最大TOP
                        
                        let top  = maxTop + oo;

                        item.init.max.top   = maxTop;
                        item.init.poi.x     = left;
                        item.init.poi.y     = top;
                        $nEls[item.index].style.left = left + "px";
                        $nEls[item.index].style.top  = top  + "px";
                    }
                    else{
                        let top = items[index-1].init.max.top + items[index-1].init.max.h;
                        item.init.max.top = top;
                        top = top + (item.init.max.h/2 - item.init.size.h/2);

                        item.init.poi.x = left;
                        item.init.poi.y = top;
                        $nEls[item.index].style.left = left + "px";
                        $nEls[item.index].style.top  = top + "px";
                    }
                    
                    
                    let itemPoi = __tool__.idTrunPoi(item.id);
                    item.init.point = itemPoi.point;
                };
            }
        }

        // 子节点展开收起的图标
        node_pull(items){
            $nEls = document.querySelectorAll('.xmd-node');
            recursion(items);
            return items;
            function recursion(items,parent){
                items.forEach((el,i)=>{
                    let scale_border = el.init.size.h/2 - 9.9;
                    if(el.root){
                        if(el.init.pull.l.has){
                            recursion(el.childrenRoot.left);
                            el.init.pull.l.show = true;
                            for(let ee of $nEls[el.index].childNodes){
                                if(ee.classList[0] === 'xmd-node-pull-l'){
                                    let cls = el.init.pull.l.plus?"xmd-node-pull-plus":"xmd-node-pull-remove";
                                    ee.classList.add(cls);
                                    ee.style.top = scale_border + "px";
                                    break;
                                }
                            }
                        }

                        if(el.init.pull.r.has){
                            recursion(el.childrenRoot.right);
                            el.init.pull.r.show = true;
                            for(let ee of $nEls[el.index].childNodes){
                                if(ee.classList[0] === 'xmd-node-pull-r'){
                                    let cls = el.init.pull.r.plus?"xmd-node-pull-plus":"xmd-node-pull-remove";
                                    ee.classList.add(cls);
                                    ee.style.top = scale_border + "px";
                                    break;
                                }
                            }
                        }
                        return;
                    }

                    if(el.children.length > 0){
                        recursion(el.children);
                    }
                    else{
                        // return;
                    }

                    if(el.left){
                        if(!el.init.pull.l.has)return;
                        el.init.pull.l.show = true;
                        for(let ee of $nEls[el.index].childNodes){
                            if(ee.classList[0] === 'xmd-node-pull-l'){
                                let cls = el.init.pull.l.plus?"xmd-node-pull-plus":"xmd-node-pull-remove";
                                ee.classList.add(cls);
                                ee.style.top = scale_border + "px";
                                break;
                            }
                        }
                    }
                    else{
                        if(!el.init.pull.r.has)return;
                        el.init.pull.r.show = true;
                        for(let ee of $nEls[el.index].childNodes){
                            if(ee.classList[0] === 'xmd-node-pull-r'){
                                let cls = el.init.pull.r.plus?"xmd-node-pull-plus":"xmd-node-pull-remove";
                                ee.classList.add(cls);
                                ee.style.top = scale_border + "px";
                                break;
                            }
                        }
                    }
                });

            }
        }

        // 子节点展开收起项
        node_show(){
            $nEls.forEach(el=>{
                let id                  = el.getAttribute('id');
                let the                 = __DB__.nodes[id];
                if(the.root)return;

                let opacity             = the.show?"1":"0";
                let zIndex              = the.show?10:-1;
                let pointerEvents       = the.show?"auto":"none";
                el.style.opacity        = opacity;
                el.style.zIndex         = zIndex;
                el.style.pointerEvents  = pointerEvents;
            });
        }

        // 可被调用
        line(){
            this.line_init();
            this.line_draw();
        }

        // 线数据初始化
        line_init(){
            let _paths = [];
            $nodesMany.length>0&&recursion($nodesMany);
            $nodesMany_frees.length>0&&recursion($nodesMany_frees);
            
            __DB__.lines = _paths;
            function recursion(items){
                items.forEach(el=>{
                    if(el.root){
                        if(el.childrenRoot.left.length > 0){
                            rpg({
                                id:el.id,
                                label:el.label,
                                left:true,
                                children:el.childrenRoot.left,
                            });
                        }
                        
                        if(el.childrenRoot.right.length > 0){
                            rpg({
                                id:el.id,
                                label:el.label,
                                left:false,
                                children:el.childrenRoot.right,
                            });
                        }
                    }
                    else{
                        if(el.children.length > 0){
                            rpg({
                                id:el.id,
                                label:el.label,
                                left:el.left,
                                children:el.children,
                            });
                        }
                    }
                })
            }

            function rpg(item) {
                let s = {
                    id:item.id,
                    label:item.label,
                }

                if(item.left){
                    s.poi = __DB__.nodes[item.id].init.point.ad;
                }
                else{
                    s.poi = __DB__.nodes[item.id].init.point.bc;
                }

                item.children.forEach(el=>{
                    let e = {
                        id:el.id,
                        label:el.label,
                    }

                    e.poi = el.left?__DB__.nodes[el.id].init.point.bc:__DB__.nodes[el.id].init.point.ad;
                    _paths.push({
                        left:item.left,
                        s:__tool__.deepCopy(s),
                        e:__tool__.deepCopy(e),
                    });                    
                })
                if(item.children.length > 0)recursion(item.children);
            }
        }

        // 解析线数据 返回SVG的数据
        line_path(res){
            let s = {},
                e = {};

            let step = 30;
            let sPoi = res.s.poi;
            let ePoi = res.e.poi;
            
            let s_mid_y = sPoi[1].toFixed(2);
            let e_mid_y = ePoi[1].toFixed(2);
            
            return res.left?fn_left():fn_right();
    
            function fn_left(){
                s.x = sPoi[0];
                s.y = sPoi[1];
                e.x = ePoi[0];
                e.y = ePoi[1];
    
                let M  = s.x  + "," + s.y;
                let L1 = (s.x - step) + "," + s.y;
                let L2 = (s.x - step) + "," + (e.y);
                let L3 = e.x + "," + e.y;
                let N1 = `a 10,10 0 0 0 -10,-10`;
                let N2 = `a 10,10 0 0 1 -10,10`;
                let A = '';
    
                if(s_mid_y > e_mid_y){
                    L2 = (s.x - step) + "," + (e.y + 10);
                    A = N1;
                }
                else if(s_mid_y < e_mid_y){
                    L2 = (s.x - step) + "," + (e.y - 10);
                    A = N2;
                }
    
                return `M ${M} L ${L1} L ${L2} ${A} L ${L3}`;
            }
    
            function fn_right(){
                s.x = sPoi[0];
                s.y = sPoi[1];
                e.x = ePoi[0];
                e.y = ePoi[1];
    
                let M  = (s.x - 0)  + "," + s.y;
                let L1 = (s.x + step) + "," + s.y;
                let L2 = (s.x + step) + "," + (e.y);
                let L3 = e.x + "," + e.y;
                // let N1  = `a 10,10 0 0 1 10,-10`;
                // let N2  = `a 10,10 0 0 0 10,10`;
                let N1  = `a 10,10 0 0 1 10,-10`;
                let N2  = `a 10,10 0 0 0 10,10`;
                let A = '';
    
                if(s_mid_y > e_mid_y){
                    L2 = (s.x + step) + "," + (e.y + 10);
                    A = N1;
                }
                else if(s_mid_y < e_mid_y){
                    L2 = (s.x + step) + "," + (e.y - 10);
                    A = N2;
                }
    
                return `M ${M} L ${L1} L ${L2} ${A} L ${L3}`;
            }
            
        }

        // 绘制线
        line_draw(){
            $e = document.getElementById('canvas');
            // let oldSvg = document.getElementById('svg');
            // if(oldSvg)$e.removeChild(oldSvg);
            let w = $e.getBoundingClientRect().width;
            let h = $e.getBoundingClientRect().height;

            let colors = $init.style.line.colors;// 颜色配置
            
            let len = colors.length;
            let lineSize = $init.style.line.size;
            let html = '';
            __DB__.lines.forEach(el=>{
                // 临时写的
                let color = colors[this.num];

                if(el.execute)color=el.color;
                if(len > 1){
                    if(this.num === len - 1){
                        this.num = 0;
                    }
                    else{
                        this.num++;
                    }
                }

                let line = this.line_path(el);
                html += `
                    <path
                        fill="none"
                        stroke="${color}"
                        opacity="1"
                        stroke-width="${lineSize}"
                        pointer-events="stroke"
                        d="${line}"
                        ></path>
                `;
            });


            let htmlSvg = `
                <svg
                    class="xmd-svg"
                    id="svg"
                    width="${w}"
                    height="${h}"
                    version="1.1"
                    >${html}</svg>
                <svg
                    class="xmd-svg_contact"
                    id="svg_contact"
                    width="${w}"
                    height="${h}"
                    version="1.1"
                    >
                    </svg>
                <svg
                    class="xmd-svg"
                    id="svgTemporary"
                    width="${w}"
                    height="${h}"
                    version="1.1"
                    ></svg>
                
                <svg
                    class="xmd-svg-automatic"
                    id="automaticConnection"
                    width="${w}"
                    height="${h}"
                    version="1.1"
                    ></svg>
                <svg
                    class="xmd-svg-svgDirection"
                    id="svgDirection"
                    width="${w}"
                    height="${h}"
                    version="1.1"
                    ></svg>
            `;
            let svgAllmain = document.getElementById('svgAllmain');
            svgAllmain.innerHTML = htmlSvg;
            
        }

        // 清除画布上的信息
        clear(type="node"){
            if(type === "node"){
                __Arr__.nodes = [];
                __DB__.nodes = {};

                return;
            }
            
            if(type === "line"){
                __Arr__.lines = [];
                __DB__.lines = {};

                return;
            }
        }
    }

    // 公共工具类模块
    class xTool {
        constructor(){
            
        }

        // 通过ID 获取宽高xy 和坐标点
        idPoiTrunInfos(id,poi){ 
            let obj = {
                w:0,
                h:0,
                x:0,
                y:0,
                point:{}
            };
            
            let el = document.getElementById(id);
            let rect = el.getBoundingClientRect();
            let w = rect.width;
            let h = rect.height;
            w = w/$scale;
            h = h/$scale;
            w = parseInt(w * 1000000)/1000000;
            h = parseInt(h * 1000000)/1000000;

            let x = parseFloat(poi.x);
            let y = parseFloat(poi.y);
    
            let a = [x , y];
            let b = [x + w , y];
            let c = [x + w , y + h];
            let d = [x , y + h];
            let ab = [x + w/2 , y];
            let ad = [x , y + h/2];
            let bc = [x + w , y + h/2];
            let cd = [x + w/2 , y + h];
            let mid = [x + w/2 , y + h/2];
    
            obj.w = w;
            obj.h = h;
            obj.x = x;
            obj.y = y;
            obj.point = {a,b,c,d,ab,bc,cd,ad,mid};
            
            return obj;
        }

        // 通过ID 获取宽高xy 和坐标点
        idTrunPoi(id){ 
            let obj = {
                w:0,
                h:0,
                x:0,
                y:0,
                point:{}
            };
    
            let el = document.getElementById(id);
            let rect = el.getBoundingClientRect();
            let w = rect.width;
            let h = rect.height;
            w = w/$scale;
            h = h/$scale;
            w = parseInt(w * 1000000)/1000000;
            h = parseInt(h * 1000000)/1000000;

            let x = parseFloat(el.style.left);
            let y = parseFloat(el.style.top);
    
            let a  = [x , y];
            let b  = [x + w , y];
            let c  = [x + w , y + h];
            let d  = [x , y + h];
            let ab = [x + w/2 , y];
            let ad = [x , y + h/2];
            let bc = [x + w , y + h/2];
            let cd = [x + w/2 , y + h];
            let mid = [x + w/2 , y + h/2];
    
            obj.w = w;
            obj.h = h;
            obj.x = x;
            obj.y = y;
            obj.point = {a,b,c,d,ab,bc,cd,ad,mid};
            
            return obj;
        }

        //数组对象深拷贝
        deepCopy(source){
            var sourceCopy = source instanceof Array ? [] : {};
                sourceCopy = source===null?null:sourceCopy;
            for (var item in source) {
                sourceCopy[item] = typeof source[item] === 'object' ? this.deepCopy(source[item]) : source[item];
            }
            return sourceCopy;
        }

        // 深度合并对象
        extends(a, b) { 
            for (var k in b) {
                if(a.hasOwnProperty(k)){
                    if(typeof b[k] === "object"){
                        if(b[k] instanceof Array){
                            a[k] = b[k];
                        }
                        else if(b[k] instanceof Function){
                            a[k] = b[k];
                        }
                        else if(Object.keys(b[k] > 0)){
                            this.extends(a[k],b[k]);
                        }
                        
                    }
                    else{
                        a[k] = b[k];
                    }
                    
                }

            }
            return a;
        }

        extends2(a, b) { 
            for (var key in b) {
                a[key] = a[key] && a[key].toString() === "[object Object]" ?
                    this.extends(a[key], b[key]) : a[key] = b[key];
            }
            return a;
        }

        // 防抖
        antiShake(fn,delay){
            let timer = null;
            return ()=>{
                timer&&clearTimeout(timer);
                timer=setTimeout(fn.bind(this, arguments),delay);
            }
        }

        // 节流
        throttle(fn,delay){
            let timer = null;
            return ()=>{
                if(timer!==null)return;
                timer=setTimeout(()=>{
                    timer = null;
                    return fn.apply(this, arguments);
                },delay);
            }
        }

        
    }

    // 拖拽模块
    class xDraw {
        constructor(){
            this.switch = false;

            // 节点拖拽起始座标（屏幕）
            this.x = 0;
            this.y = 0;

            //鼠标拖拽后的距离差值
            this.diffXY = {
                x:0,y:0
            };

            this.drawInfo       = null;//拖拽节点的额外信息
            this.drawId         = null;//拖拽节点的id
            this.drawPid        = null;//拖拽节点的新pid
            this.drawType       = null;//拖拽的类型(in,up,down,free)
            this.drawTypeRoot   = null;//目标节点是否是根节点
            this.drawNewNode    = false;//拖拽出新节点
            this.drawEvent      = null;//拖拽元素
            this.drawOldData    = null;//拖拽节点的旧数据
            this.elDraw         = null;
            this.hasInCanvas    = false;// 是否在画布内

            //禁放区域
            this.va = {
                w:0,
                h:0,
                x:0,
                y:0,
            }
        }

        // 初始
        use(){
            // this.node();
            this.nodeUp();
            this.nodeDown();
            this.nodeIn();
            this.drawId = null;
            this.drawPid = null;
            this.drawType = null;
            this.drawTypeRoot = null;
        }

        // 拖拽对象
        drawObj(res){
            let id      = res.id;
            let label   = res.label;
            let type    = res.type;
            

            if(type === "add"){
                this.elDraw = null;
                this.elDraw = document.createElement('div');
                this.elDraw.setAttribute("id",'drawDiv');
                this.elDraw.setAttribute('class','xmd-drawDiv');
                this.elDraw.setAttribute('pid',id);
                this.elDraw.style.opacity = 0;
                this.elDraw.innerHTML = label;
                
                let w = document.getElementById(id).getBoundingClientRect().width;
                let h = document.getElementById(id).getBoundingClientRect().height;
                this.elDraw.style.width  = w + "px";
                this.elDraw.style.height = h + "px";
                this.elDraw.style.lineHeight = h + "px";
                this.elDraw.style.zIndex = -1;
                document.body.appendChild(this.elDraw);
                // this.visibleArea();// 拖拽区域-离开此区域会生成自由节点
                return;
            }

            if(type === "del"){
                document.body.removeChild(this.elDraw);
                this.elDraw = null;
                this.nodeDisabledObj({type:'del'});// 删除禁选区域（有子节点的节点使用）  
                // this.visibleArea('hide');// 拖拽区域-离开会生成自由节点
                return;
            }

            if(type === "other"){
                this.drawInfo = {
                    id,label,
                    type:res.nodeType,
                    html:res.html||null,
                }
                createdDiv.call(this,{w:100,h:50});
                return;
            }

            let x = res.x;
            let y = res.y;
            this.elDraw.style.left      = x + "px";
            this.elDraw.style.top       = y + "px";
            this.elDraw.style.opacity   = 1;
            this.elDraw.style.zIndex    = 1;
            if(__draw__.drawInfo === null)return;
            __draw__.drawInfo.x = x;
            __draw__.drawInfo.y = y;

            function createdDiv(obj){
                this.elDraw = null;
                this.elDraw = document.createElement('div');
                this.elDraw.setAttribute("id",'drawDiv');
                this.elDraw.setAttribute('class','xmd-drawDiv');
                // this.elDraw.setAttribute('pid',id);
                this.elDraw.style.opacity   = 0;
                this.elDraw.innerHTML       = label;

                let w = obj.w;
                let h = obj.h;
                this.elDraw.style.width  = w + "px";
                this.elDraw.style.height = h + "px";
                this.elDraw.style.lineHeight = h + "px";
                this.elDraw.style.textAlign = "center";
                document.body.appendChild(this.elDraw);

                const nodesIn = document.querySelectorAll('.xmd-node-in');
                for(let el of nodesIn){
                    el.style.display = "block";
                }
            }
        }

        // 禁止拖至子节点的区域
        nodeDisabledObj(res){
            let nodeDisabled = document.getElementById('nodeDisabled');
            if(nodeDisabled)$e.removeChild(nodeDisabled);
            if(res.type === 'del')return;

            let id = res.id;
            let nodeData = __DB__.nodes[id];
            nodeDisabled = document.createElement('div');
            nodeDisabled.setAttribute('id','nodeDisabled');
            nodeDisabled.setAttribute('class','nodeDisabled');
            let x       = nodeData.init.poi.x,
                w       = nodeData.init.size.w,
                maxw    = nodeData.init.max.w,
                maxh    = nodeData.init.max.h + 10;
            // let left = nodeData.left?x-maxw-40:x+w+40;
            // let left = nodeData.left?x - maxw + w + (40):x - (40);
            let left = nodeData.left?x - maxw+w:x - (40);
                
            nodeDisabled.style.width = maxw + "px";
            nodeDisabled.style.height = maxh + "px";
            // nodeDisabled.style.background = $init.style.noSelectionArea.bgColor;
            nodeDisabled.style.backgroundColor = "rgba(255,255,255,0.8)";
            nodeDisabled.style.position = "absolute";
            nodeDisabled.style.zIndex = "999";
            nodeDisabled.style.left = left + "px";
            // nodeDisabled.style.top = (nodeData.init.max.top - 5) + "px";
            nodeDisabled.style.top = (nodeData.init.poi.y + nodeData.init.size.h/2 - nodeData.init.max.h/2) + "px";
            // nodeDisabled.style.zIndex = 23;
            // nodeDisabled.style.pointerEvents = 'none';
            $e.appendChild(nodeDisabled);
            this.va = {
                w:maxw,
                h:maxh,
                x:left,
                y:nodeData.init.max.top,
            }

            nodeDisabled.onmousedown = (e)=>{
                e.stopPropagation();
                e.preventDefault();
            }
        }

        // 可拖拽节点点击事件
        nodeMouseDown(res){
            let e       = res.e,
                el      = res.el,
                id      = el.getAttribute('id'),
                label   = __DB__.nodes[id].label;

            if(e.button == 2)return;// 过滤右键
            if(__DB__.nodes[id].root)return;// 过滤根节点
            this.x = e.clientX;
            this.y = e.clientY;
            
            this.drawObj({type:"add",id,label});//生成拖拽对象
            let boxW = e.target.getBoundingClientRect().width;
            
            
            // +-按钮事件穿透
            let pull_r = document.querySelectorAll('.xmd-node-pull-r');
            let pull_l = document.querySelectorAll('.xmd-node-pull-l');
            pull_r.forEach(el=>{
                el.style.pointerEvents = "none";
            });
            
            pull_l.forEach(el=>{
                el.style.pointerEvents = "none";
            });


            let ox = e.offsetX*$scale;
            let oy = e.offsetY*$scale;

            // 标记变量-用于单击使用
            let onceOffMove = 0;
            let onceOffUp   = true;
            document.onmousemove = (ev)=>{
                let x = ev.clientX - ox,
                    y = ev.clientY - oy;
                if(onceOffMove < 3){
                    onceOffMove++;
                    return;
                }

                onceOffUp = false;
                this.drawId = id;

                const nodesIn = document.querySelectorAll('.xmd-node-in');
                for(let el of nodesIn){
                    el.style.display = "block";
                }

                this.drawObj({x,y});// 设置拖拽对象位置
                // this.nodeDisabledObj({id});// 生成禁选区域（有子节点的节点使用） 
            }

            document.onmouseup = (ev)=>{
                document.onmousemove = null;
                document.onmouseup   = null;
                $s.onmouseout        = null;
                $s.onmouseover      = null;

                this.drawObj({type:"del"});// 删除拖拽对象       
                this.clear(); // 清理拖拽

                const nodesIn = document.querySelectorAll('.xmd-node-in');
                for(let el of nodesIn){
                    el.style.display = "none";
                }
                
                if(onceOffUp)return;
                onceOffUp = true;

                window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
                
                this.x = ev.offsetX - e.offsetX;
                this.y = ev.offsetY - e.offsetY;

                // this.diffXY.x = ev.clientX - this.x;
                // this.diffXY.y = ev.clientY - this.y;

                // +-按钮事件穿透
                pull_r.forEach(el=>{
                    el.style.pointerEvents = "auto";
                });

                pull_l.forEach(el=>{
                    el.style.pointerEvents = "auto";
                });

                this.up();// 拖拽结束
            }

            $s.onmouseout = (ev)=>{
                this.hasInCanvas = false;
            }

            $s.onmouseover = (ev)=>{
                this.hasInCanvas = true;
            }
            
        }

        // 校验是否可拖到的节点
        checkDrawId(){
            let drawId      = this.drawId,
                drawPid     = this.drawPid;
            
            this.drawType = "own";
            let data_drawId     = __tool__.deepCopy(__DB__.nodes[drawId]);
            // let data_drawPid    = __tool__.deepCopy(__DB__.nodes[drawPid]);

            if(this.drawNewNode)return true;

            if(drawId === drawPid){
                return false;
            }

            if(data_drawId.nodeType==="free"){
                if(!$nodesMany_ids[drawId]){
                    return false;
                }
    
                if($nodesMany_ids[drawId].includes(drawPid)){
                    return false;
                }
                return true;
            }

            if(!$nodesMany_ids[drawId]){
                return false;
            }

            if($nodesMany_ids[drawId].includes(drawPid)){
                return false;
            }

            return true;
        }
        
        // 进入节点
        nodeIn(){
            const nodesIn = document.querySelectorAll('.xmd-node-in');
            for(let el of nodesIn){
                el.addEventListener('mouseover',(e)=>{
                    e.stopPropagation();
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    let id = el.parentNode.getAttribute('id');

                    this.drawPid = id;
                    let isNext = this.checkDrawId();
                    if(!isNext){
                        this.drawPid = null;
                        return;
                    }
                    
                    el.classList.add('active');

                    let x = parseFloat(el.parentNode.style.left),
                        y = parseFloat(el.parentNode.style.top),
                        w = parseFloat(el.parentNode.getBoundingClientRect().width)/$scale,
                        h = parseFloat(el.parentNode.getBoundingClientRect().height)/$scale;
                    
                    this.drawPid = id;
                    this.drawType = "in";
                    if(__DB__.nodes[id].root){
                        let theN = __DB__.nodes[id];
                        let w_root = theN.init.size.w;
                        el.onmousemove = (evv)=>{
                            let ox = evv.offsetX;
                            if(ox<=0 || ox >=w)return;
                            
                            this.vBox({show:false});
                            __line__.del({id:"vline"});
                            if(ox > w_root/2){
                                this.drawType = "in";
                                this.drawTypeRoot = "right";
                                fn_right.call(this,{x,y,w,h});
                            }
                            else{
                                this.drawTypeRoot = "left";
                                fn_left.call(this,{x,y,w,h});
                            }
                        }
                        el.onmouseup = ()=>{
                            el.onmousemove = null;
                            el.onmouseup = null;
                        }
                        return;
                    }
                    
                    let left = __DB__.nodes[id].left;
                    if(left){
                        fn_left.call(this,{x,y,w,h});
                    }
                    else{
                        fn_right.call(this,{x,y,w,h});
                    }
                    
                });

                el.addEventListener('mouseout',(e)=>{
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    this.drawPid = null;
                    this.drawType = 'free';
                    this.clear();
                });
            }

            function fn_left(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w,
                    h = res.h;
                
                let x1 = x,
                    y1 = y + h/2,
                    x2 = x - 20,
                    y2 = y + h/2
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x - 90,
                    y:y + (h - 20)/2,
                    show:true
                });
            }

            function fn_right(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w,
                    h = res.h;
                let x1 = x + w,
                    y1 = y + h/2,
                    x2 = x + w + 20,
                    y2 = y + h/2;
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x + w + 20,
                    y:y + (h - 20)/2,
                    show:true
                });
            }
        }

        // 进入节点上方
        nodeUp(){
            const nodesUp = document.querySelectorAll('.xmd-node-up');
            for(let el of nodesUp){
                el.addEventListener('mouseover',(e)=>{
                    e.stopPropagation();
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    let id = el.parentNode.getAttribute('id');
                    if(__DB__.nodes[id].nodeType === "free")return;
                    if(__DB__.nodes[id].root)return;

                    this.drawPid = id;
                    let isNext = this.checkDrawId();
                    if(!isNext){
                        this.drawPid = null;
                        return;
                    }

                    this.drawType = 'up';

                    let drawPid = elDraw.getAttribute('pid');
                    if(drawPid === id)return;
                    el.classList.add('active');

                    for(let item of el.parentNode.childNodes){
                        if(item.classList[0]==="xmd-node-in"){
                            item.classList.add('active');
                            break;
                        }
                    }

                    let x = parseFloat(el.parentNode.style.left),
                        y = parseFloat(el.parentNode.style.top),
                        w = parseFloat(el.parentNode.getBoundingClientRect().width),
                        h = parseFloat(el.parentNode.getBoundingClientRect().height);
                    
                    let left = __DB__.nodes[id].left;
                    if(left){
                        fn_left.call(this,{x,y,w,h});
                    }
                    else{
                        fn_right.call(this,{x,y,w,h});
                    }

                });

                el.addEventListener('mouseout',(e)=>{
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    this.drawPid = null;
                    this.drawType = 'free';
                    this.clear();
                });
            }

            function fn_left(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w/$scale,
                    h = res.h/$scale;
                
                let x1 = x,
                    y1 = y - 5,
                    x2 = x - 20,
                    y2 = y - 10
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x - 90,
                    y:y - 20,
                    show:true
                });
            }

            function fn_right(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w/$scale,
                    h = res.h/$scale;
                
                let x1 = x + w,
                    y1 = y - 5,
                    x2 = x + w + 20,
                    y2 = y - 10
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x + w + 20,
                    y:y - 20,
                    show:true
                });
            }
        }

        // 进入节点下方
        nodeDown(){
            const nodesDown = document.querySelectorAll('.xmd-node-down');
            for(let el of nodesDown){
                el.addEventListener('mouseover',(e)=>{
                    e.stopPropagation();
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    let id = el.parentNode.getAttribute('id');
                    if(__DB__.nodes[id].nodeType === "free")return;
                    if(__DB__.nodes[id].root)return;

                    this.drawPid = id;
                    let isNext = this.checkDrawId();
                    if(!isNext){
                        this.drawPid = null;
                        return;
                    }

                    el.classList.add('active');
                    
                    this.drawPid = id;
                    this.drawType = "down";
                    for(let item of el.parentNode.childNodes){
                        if(item.classList[0]==="xmd-node-in"){
                            item.classList.add('active');
                            break;
                        }
                    }

                    let x = parseFloat(el.parentNode.style.left),
                        y = parseFloat(el.parentNode.style.top),
                        w = parseFloat(el.parentNode.getBoundingClientRect().width),
                        h = parseFloat(el.parentNode.getBoundingClientRect().height);

                    let left = __DB__.nodes[id].left;
                    if(left){
                        fn_left.call(this,{x,y,w,h});
                    }
                    else{
                        fn_right.call(this,{x,y,w,h});
                    }
                });

                el.addEventListener('mouseout',(e)=>{
                    let elDraw = document.getElementById('drawDiv');
                    if(!elDraw)return;
                    this.drawPid = null;
                    this.drawType = 'free';
                    this.clear();
                });
            }

            function fn_left(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w/$scale,
                    h = res.h/$scale;
                
                let x1 = x,
                    y1 = y + h + 5,
                    x2 = x - 20,
                    y2 = y + h +10 
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x - 90,
                    y:y + h,
                    show:true
                });
            }

            function fn_right(res) {
                let x = res.x,
                    y = res.y,
                    w = res.w/$scale,
                    h = res.h/$scale;

                let x1 = x + w,
                    y1 = y + h + 5,
                    x2 = x + w + 20,
                    y2 = y + h + 10
                
                __line__.add({
                    id:"vline",
                    x1,y1,x2,y2,
                });
                    
                this.vBox({
                    x:x + w + 20,
                    y:y + h,
                    show:true
                });
            }
        }

        // 节点的拖拽的上下区域是否显示 
        nodeDrawMain(res){
            let type = res.type;
            if(type === 'show'){
                let nodeUps = document.querySelectorAll('.xmd-node-up');
                for(let el of nodeUps){
                    el.style.display = "block";
                }

                let nodeDown = document.querySelectorAll('.xmd-node-down');
                for(let el of nodeDown){
                    el.style.display = "block";
                }
                return;
            }
            
            if(type === 'hide'){
                let nodeUps = document.querySelectorAll('.xmd-node-up');
                for(let el of nodeUps){
                    el.style.display = "none";
                }

                let nodeDown = document.querySelectorAll('.xmd-node-down');
                for(let el of nodeDown){
                    el.style.display = "none";
                }
                return;
            }
        }

        // 拖拽时的临时模块-预显示
        vBox(res){
            if(!res.show){
                let elBox = document.getElementById('vbox');
                if(elBox)$e.removeChild(elBox);
                return;
            }

            let x = res.x,
                y = res.y;
            let elBox = document.createElement('div');
            elBox.setAttribute('id','vbox');
            elBox.setAttribute('class','xmd-vbox');
            elBox.style.left = x + 'px';
            elBox.style.top  = y + 'px';
            $e.appendChild(elBox);
        }

        // 清理重置拖拽时的临时模块
        clear(){
            let nodeIns = document.querySelectorAll('.xmd-node-in');
            let nodeUps = document.querySelectorAll('.xmd-node-up');
            let nodeDowns = document.querySelectorAll('.xmd-node-down');
            nodeIns.forEach(el=>{
                el.setAttribute('class','xmd-node-in');
            });

            nodeUps.forEach(el=>{
                el.setAttribute('class','xmd-node-up');
            });

            nodeDowns.forEach(el=>{
                el.setAttribute('class','xmd-node-down');
            });

            this.vBox({show:false});// 清理拖拽时预显示块
            __line__.del({id:"vline"});// 清理拖拽时预显示线

            
        }

        // 拖拽节点
        up(res){
            let drawId      = this.drawId,
                drawPid     = this.drawPid,
                drawType    = this.drawType;
            
            if(drawType === "own")return;
            
            let data_drawId     = __tool__.deepCopy(__DB__.nodes[drawId]);
            let data_drawPid    = __tool__.deepCopy(__DB__.nodes[drawPid]);
            let index_id        = parseInt(data_drawId.index);
            let index_pid       = parseInt(data_drawPid.index);

            // 拖拽节点是新节点
            if(this.drawNewNode){
                if(!this.hasInCanvas && drawType === "free")return;
                fn_drawNewNode.call(this);
            }

            if(drawType === "in"){
                if(drawPid !== data_drawId.pid){
                    fn.call(this);
                }
                else{
                    if(data_drawPid.root){
                        let leftRoot = this.drawTypeRoot === "left"?true:false;
                        if(data_drawId.left !== leftRoot){
                            fn.call(this);
                        }
                    }
                }

                function fn(){
                    data_drawId.pid      = drawPid;
                    data_drawId.left     = data_drawPid.left;
                    data_drawId.nodeType = "node";
                    
                    if(__DB__.nodes[drawPid].root){
                        data_drawId.left = this.drawTypeRoot === "left"?true:false;
                    }

                    __DB__.nodes[drawId].pid                = data_drawId.pid;
                    __DB__.nodes[drawId].left               = data_drawId.left;
                    __DB__.nodes[drawId].styleInit.priority = data_drawId.styleInit.priority;

                    __Arr__.nodes.splice(index_id,1);
                    __Arr__.nodes.splice(__Arr__.nodes.length,0,data_drawId);
                }
                
            }
            else if(drawType === "up"){
                __Arr__.nodes.splice(index_id,1);
                
                data_drawId.pid = data_drawPid.pid;
                data_drawId.left = data_drawPid.left;
                data_drawId.nodeType = "node";

                let i = index_pid > index_id?index_pid-1:index_pid;
                __Arr__.nodes.splice(i,0,data_drawId);
                __DB__.nodes[drawId].pid                = data_drawId.pid;
                __DB__.nodes[drawId].left               = data_drawId.left;
                __DB__.nodes[drawId].styleInit.priority = data_drawId.styleInit.priority;
            }
            else if(drawType === "down"){
                __Arr__.nodes.splice(index_id,1);

                data_drawId.pid = data_drawPid.pid;
                data_drawId.left = data_drawPid.left;
                data_drawId.nodeType = "node";

                let i = index_pid < index_id?index_pid+1:index_pid;
                __Arr__.nodes.splice(i,0,data_drawId);
                __DB__.nodes[drawId].pid                = data_drawId.pid;
                __DB__.nodes[drawId].left               = data_drawId.left;
                __DB__.nodes[drawId].styleInit.priority = data_drawId.styleInit.priority;
            }
            else if(this.drawNewNode){
                data_drawId.nodeType = "free";

                let x = this.x,
                    y = this.y;
                
                
                __Arr__.nodes[index_id].init.poi.x = x;
                __Arr__.nodes[index_id].init.poi.y = y;
                __Arr__.nodes[index_id].pid = null;
                __Arr__.nodes[index_id].nodeType = "free";

                __DB__.nodes[drawId].init.poi.x = x;
                __DB__.nodes[drawId].init.poi.y = y;
                __DB__.nodes[drawId].pid = null;
                __DB__.nodes[drawId].nodeType = "free";
            }
            else{
                if(!this.hasInCanvas)return;
                data_drawId.nodeType = "free";

                let x_poi = __Arr__.nodes[index_id].init.poi.x;
                let y_poi = __Arr__.nodes[index_id].init.poi.y;
                let x_diff = this.diffXY.x;
                let y_diff = this.diffXY.y;
                
                x_diff = x_diff/$scale;
                y_diff = y_diff/$scale;

                let x = this.x,
                    y = this.y;
                
                $scale = parseFloat($scale);
                
                __Arr__.nodes[index_id].init.poi.x = x;
                __Arr__.nodes[index_id].init.poi.y = y;
                __Arr__.nodes[index_id].pid = null;
                __Arr__.nodes[index_id].nodeType = "free";

                __DB__.nodes[drawId].init.poi.x = x;
                __DB__.nodes[drawId].init.poi.y = y;
                __DB__.nodes[drawId].pid = null;
                __DB__.nodes[drawId].nodeType = "free";
            }

            // 拖动节点
            $opt.nodeDrag =  __tool__.deepCopy(__DB__.nodes[drawId]);
            window.dispatchEvent($myEvent.nodeDrag);

            __Arr__.nodes.forEach((el,i)=>{
                __DB__.nodes[el.id].index = i;
                el.index = i;
            });
            __canvas__.load();
            
            // 拖拽节点是新节点
            function fn_drawNewNode(){
                drawId = (new Date()).getTime() + "_node";
                let styleInit = {};
                styleInit.nodeStyleCls  = $init.nodePriorityCls[this.drawInfo.type].nodeStyleCls;
                styleInit.nodeIcon      = $init.nodePriorityCls[this.drawInfo.type].nodeIcon;

                __format__.nodeInit({
                    label   : this.drawInfo.label,
                    id      : drawId, 
                    pid     : data_drawPid,
                    left    : this.drawTypeRoot === "left"?true:false,
                    html    : this.drawInfo.html||null,
                    type    : this.drawInfo.type||"rule",
                    styleInit
                });
                data_drawId = __DB__.nodes[drawId];
                data_drawId.index = __Arr__.nodes.length - 1;
                index_id = data_drawId.index;
                $opt.nodeAdd = __tool__.deepCopy(data_drawId);
                window.dispatchEvent($myEvent.nodeAdd);     // 新增节点
                window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            }
        }

        // 拖拽节点
        up11111(){
            let id      = this.drawId,
                pid     = this.drawPid,
                type    = this.drawType;
            if(id === pid && type !== "free")return;
            
            // 被拖拽的节点
            let theDiv = __tool__.deepCopy(__DB__.nodes[id]);
            let i_id = parseInt(theDiv.index);

            // 被拖拽节点的父节点
            let opid = __tool__.deepCopy(__DB__.nodes[pid]);
            let i_pid = parseInt(opid.index);

            let lleft = this.drawTypeRoot==="left"?true:false;
            if(id === null){
                id = "newOther_" + (new Date()).getTime();
                let styleInit = {};
                styleInit.nodeStyleCls  = $init.nodePriorityCls[this.drawInfo.type].nodeStyleCls;
                styleInit.nodeIcon      = $init.nodePriorityCls[this.drawInfo.type].nodeIcon;

                __format__.nodeInit({
                    label   : this.drawInfo.label,
                    id      : id, 
                    pid     : pid,
                    left    : lleft,
                    html    : this.drawInfo.html||null,
                    type    : this.drawInfo.type||"rule",
                    styleInit
                });
                theDiv = __DB__.nodes[id];
                theDiv.index = __Arr__.nodes.length - 1;
                i_id = theDiv.index;
                $opt.nodeAdd = __tool__.deepCopy(theDiv);
                window.dispatchEvent($myEvent.nodeAdd);     // 新增节点
                window.dispatchEvent($myEvent.undoRedoOpt);// 撤消重做需要的事件回调
                window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
            }

            // 一级节点不可拖拽的条件
            if(opid.root){
                if(theDiv.pid === pid && lleft === theDiv.left && this.drawId){
                    return;
                }
            }
            // 其他节点不可拖拽的条件
            else if(theDiv.pid === pid && type === "in" && theDiv.left === opid.left && this.drawId){return};

            // 旧的父节点
            let old_pid = theDiv.pid;
            let oo_old_pid = __DB__.nodes[old_pid];

            if(type === "in"){
                theDiv.pid = pid;
                theDiv.left = opid.left;
                theDiv.nodeType = "node";
                
                if(__DB__.nodes[pid].root){
                    theDiv.left = this.drawTypeRoot === "left"?true:false;
                }

                if(i_id){
                    __Arr__.nodes.splice(i_id,1);
                    __Arr__.nodes.splice(__Arr__.nodes.length,0,theDiv);
                }
                __DB__.nodes[id].pid = theDiv.pid;
                __DB__.nodes[id].left = theDiv.left;
                __DB__.nodes[id].styleInit.priority = theDiv.styleInit.priority;
            }
            else if(type === "up"){
                __Arr__.nodes.splice(i_id,1);
                
                theDiv.pid = opid.pid;
                theDiv.left = opid.left;
                theDiv.nodeType = "node";

                let i = i_pid > i_id?i_pid-1:i_pid;
                __Arr__.nodes.splice(i,0,theDiv);
                __DB__.nodes[id].pid = theDiv.pid;
                __DB__.nodes[id].left = theDiv.left;
                __DB__.nodes[id].styleInit.priority = theDiv.styleInit.priority;
            }
            else if(type === "down"){
                __Arr__.nodes.splice(i_id,1);

                theDiv.pid = opid.pid;
                theDiv.left = opid.left;
                theDiv.nodeType = "node";

                let i = i_pid < i_id?i_pid+1:i_pid;
                __Arr__.nodes.splice(i,0,theDiv);
                __DB__.nodes[id].pid = theDiv.pid;
                __DB__.nodes[id].left = theDiv.left;
                __DB__.nodes[id].styleInit.priority = theDiv.styleInit.priority;
            }
            // else{
            //     // __Arr__.nodes = __Arr__.nodes.filter(item=>{
            //     //     return item.id !== id;
            //     // })
            // }

            else if(type === "free"){
                
                theDiv.nodeType = "free";
                let _root = {}
                for(let el of __Arr__.nodes){
                    if(el.root){
                        _root = el;
                        break;
                    }
                }

                // 获取容器的宽高
                let ximdW = $s.getBoundingClientRect().width;
                let ximdH = $s.getBoundingClientRect().height;

                // 临时使用
                /*
                    使用drawInfo  保存拖拽时的屏幕坐标
                */
                    
                this.drawInfo.x = this.drawInfo.x*$scale;
                this.drawInfo.y = this.drawInfo.y*$scale;

                let drawEventY = (this.drawEvent.clientY)*$scale - 8;

                let diffX = this.drawInfo.x - __canvas__.x;
                let diffY = this.drawInfo.y - drawEventY - __canvas__.y;

                let x = (ximdW*$init.canvasScale/2 - ximdW/2) + diffX;
                let y = ximdH*$init.canvasScale/2 - ximdH/2 + diffY;

                __Arr__.nodes[i_id].init.poi.x = x;
                __Arr__.nodes[i_id].init.poi.y = y;
                __DB__.nodes[id].init.poi.x = x;
                __DB__.nodes[id].init.poi.y = y;
            }

            // 自由节点
            else{
                
                theDiv.nodeType = "free";
                let x_poi = __Arr__.nodes[i_id].init.poi.x;
                let y_poi = __Arr__.nodes[i_id].init.poi.y;
                let x_diff = this.diffXY.x;
                let y_diff = this.diffXY.y;
                
                x_diff = x_diff/$scale;
                y_diff = y_diff/$scale;

                let x = x_poi + x_diff,
                    y = y_poi + y_diff;
                
                $scale = parseFloat($scale);
                
                let va = this.va;// 禁放区域
                // 如果拖拽后在中心区域内就取消拖拽
                if(va.w === 0 && va.h === 0)return;
                
                va.x = parseInt(va.x);
                va.y = parseInt(va.y);
                va.w = parseInt(va.w);
                va.h = parseInt(va.h);
                x = parseInt(x);
                y = parseInt(y);
                if((x > va.x && x < va.x + va.w) && (y+40 > va.y && y+40 < va.y + va.h))return;

                __Arr__.nodes[i_id].init.poi.x = x;
                __Arr__.nodes[i_id].init.poi.y = y;
                __Arr__.nodes[i_id].pid = null;

                __DB__.nodes[id].init.poi.x = x;
                __DB__.nodes[id].init.poi.y = y;
                __DB__.nodes[id].pid = null;
            }

            // 拖动节点
            $opt.nodeDrag =  __tool__.deepCopy(__DB__.nodes[id]);
            window.dispatchEvent($myEvent.nodeDrag);     

            // 判断旧的父节点还有没有子节点
            let has = false;
            for(let eee of __Arr__.nodes){
                if(eee.pid === old_pid){
                    has = true;
                    break;
                }
            }

            if(oo_old_pid){
                if(oo_old_pid.left){
                    oo_old_pid.init.pull.l.has = has;
                    oo_old_pid.init.pull.r.has = !has;
    
                    __DB__.nodes[old_pid].init.pull.l.has = has;
                    __Arr__.nodes[oo_old_pid.index].init.pull.l.has = has;
                    __DB__.nodes[old_pid].init.pull.r.has = !has;
                    __Arr__.nodes[oo_old_pid.index].init.pull.r.has = !has;
                }
                else{
                    oo_old_pid.init.pull.r.has = has;
                    oo_old_pid.init.pull.l.has = !has;
    
                    __DB__.nodes[old_pid].init.pull.r.has = has;
                    __Arr__.nodes[oo_old_pid.index].init.pull.r.has = has;
                    __DB__.nodes[old_pid].init.pull.l.has = !has;
                    __Arr__.nodes[oo_old_pid.index].init.pull.l.has = !has;
                }
            }

            __Arr__.nodes.forEach((el,i)=>{
                __DB__.nodes[el.id].index = i;
                el.index = i;
            });

            //渲染节点---渲染两次-临时代码
            __canvas__.load();
            __canvas__.load();
            
        }

        // 禁放区域
        visibleArea(type){
            if(type == 'hide'){
                let vaEl = document.getElementById('vaEl');
                if(vaEl){
                    $e.removeChild(vaEl);
                }
                return;
            }

            let rootItem = null;
            for(let el of __Arr__.nodes){
                if(el.root){
                    rootItem = el;
                    break;
                }
            }
            if(rootItem === null)return;

            let max = rootItem.init.maxRoot;
            let poi = rootItem.init.point.mid;

            let w = max.l.w + max.r.w + rootItem.init.size.w + $init.style.noSelectionArea.borderWidth;
            let h = max.l.h > max.r.h ? max.l.h : max.r.h;
            let x = poi[0] - max.l.w - $init.style.noSelectionArea.borderWidth;
            let y = poi[1] - h/2;

            let vaEl = document.getElementById('vaEl');
            if(vaEl){$e.removeChild(vaEl)}

            vaEl = document.createElement('div');
            vaEl.setAttribute('id','vaEl');
            vaEl.style.width    = (w + 0) + "px";
            vaEl.style.height   = (h + 100) + "px";
            vaEl.style.top      = (y - 50) + "px";
            vaEl.style.left     = x + "px";

            vaEl.style.backgroundColor = $init.style.noSelectionArea.bgColor;
            vaEl.style.position = 'absolute';
            vaEl.style.zIndex = '-2';

            this.va = {w,h,x,y};//禁放区域

            $e.appendChild(vaEl);
        }
    }

    // 右键
    class xContentMenu {
        constructor(){
            this.id     = null;     // 被操作的ID
            this.hasDefaultOpts = {
                has:true,
                type:"node",
            };
            this.x      = 0;
            this.y      = 0;
            this.items  = [];       // 右键显示的项
            this.type   = "node";   // 右键显示的项类型
            this.lists  = {         // 右键的项集合-通过this.type来匹配
                node:[
                    {label:"添加节点",id:"add",name:"node",icon:"xmd-icon-cntMenu-add"},
                    {label:"删除节点",id:"del",name:"node",icon:"xmd-icon-cntMenu-del"},
                    // {label:"开始关联",id:"contect",name:"node",icon:"xmd-icon-cntMenu-add"},
                    // {label:"取消关联",id:"uncontect",name:"node",icon:"xmd-icon-cntMenu-del"},
                    // {label:"选中线",id:"checkLine",name:"node",icon:"xmd-icon-cntMenu-del"},
                    // {label:"截图",id:"cutImg",name:"node"},
                    // {label:"关联连线",id:"line",name:"node"},
                ],
                contactLine:[
                    {label:"删除关联线",id:"del",name:"contactLine"}
                ],
                canvas:[
                    {label:"开启连线",id:"line1",name:"canvas",is:false},
                ]
            },
            this.line = {
                id:null,
                mid:null,
                data:{},
            };

            this.addItem = $init.contextMenu.addItem;
        }

        // 设置右键选项
        /*
            res:{
                type:"canvas",// 类型（canvas、node、contactLine）
                items:[
                    {label:"",id:"",name:"canvas"}
                ],
            }
        */
        setItemfn(res){
            if(res.type === "canvas"){
                this.lists.canvas = res.items.map(item=>{
                    let obj = {
                        label:item.label,
                        id:item.id,
                        name:item.name,
                        line:item.line||null,
                        icon:item.icon||null,
                        is:item.is,
                        fn:item.fn
                    };
                    return obj;
                });
                
                return;
            }

            if(res.type === "node"){
                let arr = [];
                if(this.hasDefaultOpts.has){
                    arr = [
                        {label:"添加节点",id:"add",name:"node",icon:"xmd-icon-cntMenu-add"},
                        {label:"删除节点",id:"del",name:"node",icon:"xmd-icon-cntMenu-del"},
                        // {label:"截图",id:"cutImg",name:"node"},
                    ]
                }
                
                res.items.map(item=>{
                    let obj = {
                        label:item.label,
                        id:item.id,
                        name:item.name,
                        line:item.line||null,
                        icon:item.icon||null,
                        is:item.is,
                        fn:item.fn
                    };
                    arr.push(obj);
                });
                
                
                this.lists.node = arr;
                return;
            }

        }

        addItemfn(){
            this.addItem = $init.contextMenu.addItem;
            let addItem = this.addItem;
            if(addItem.length === 0)return;

            for(let el of addItem){
                this.lists.node.push({
                    label:el.label,
                    id:el.key,
                    name:"node",
                })
            }
        }

        use(res){
            this.init(res);// 初始化配置
            this.clear();// 创建右键菜单
            this.creat();
        }

        // 创建右键菜单
        creat(){
            let divClass = $init.style.contextMenu.class?" "+$init.style.contextMenu.class:"";
            let cntEl = document.createElement('div');
            cntEl.setAttribute('id','cntMenu');
            cntEl.setAttribute('class','xmd-contentMenu'+divClass);
            cntEl.style.cssText = `
                top             : ${this.y}px;
                left            : ${this.x}px;
            `;

            cntEl.onclick = (e)=>{
                e.stopPropagation();
                e.preventDefault();
            }

            let iEls = this.createItems();// 创建菜单项
            if(!iEls)return;
            iEls.forEach(el=>{
                cntEl.appendChild(el);
            })

            document.body.append(cntEl);
            document.onclick = ()=>{
                this.clear();
                document.onclick = null;
            }

            // 右键菜单位置自适应
            let win_w = $s.getBoundingClientRect().width;
            let win_h = $s.getBoundingClientRect().height;
            let box_w = cntEl.getBoundingClientRect().width;
            let box_h = cntEl.getBoundingClientRect().height;

            if(this.x + box_w >=  win_w){
                let xx = this.x - box_w;
                cntEl.style.left = xx + "px";
            }

            if(this.y + box_h >=  win_h){
                let yy = this.y - box_h;
                cntEl.style.top = yy + "px";
            }
        }

        // 初始化配置
        init(res){
            this.id = res.id;
            this.x = res.x;
            this.y = res.y;
            let type = res.type||"node";
            this.type = type;

            this.items = this.lists[type];
            
            // if(this.hasDefaultOpts.type === type){
            //     this.items = this.hasDefaultOpts.has?this.lists[type]:[];
            // }
            if(__DB__.nodes[res.id] && __DB__.nodes[res.id].root){
                this.items = this.items.filter(item=>{
                    return item.id !== "del";
                });
            }
        }

        // 创建菜单项-有返回值
        createItems(){
            let items = this.items;
            if(items.length === 0)return;
            let htmlEl = [];

            items.forEach(el=>{
                let iEl = document.createElement('div');
                iEl.setAttribute('class','xmd-cntItem');
                iEl.setAttribute('id',el.id);
                iEl.setAttribute('name',el.name);
                iEl.innerHTML = el.label;
                if(el.line){
                    if(el.line === "down"){
                        iEl.setAttribute('class','xmd-cntItem xmd-cntItem-lineDown');
                    }
                    else if(el.line === "up"){
                        iEl.setAttribute('class','xmd-cntItem xmd-cntItem-lineUp');
                    }
                }

                if(el.icon){
                    iEl.classList.add(el.icon);
                }

                htmlEl.push(iEl);

                iEl.onclick = (e)=>{
                    let type = iEl.getAttribute('id');
                    let name = iEl.getAttribute('name');
                    let data = this.lists[name];
                    data = data.filter(item=>{
                        return item.id === type;
                    });
                    data = data[0];
                    this.evt({type,name,data,e});
                    this.clear();
                }
            });

            return htmlEl;
        }

        // 清除右键菜单
        clear(){
            let cntEl = document.getElementById('cntMenu');
            if(!cntEl)return;
            document.body.removeChild(cntEl);
        }

        // 事件处理
        evt(res){
            let type = res.type,
                name = res.name,
                id   = this.id,
                data = res.data||{},
                e    = res.e;
            let node = __DB__.nodes[id];

            if(name === "node"){
                fn_node.call(this)
                return;
            }

            if(name === "contactLine"){
                fn_contactLine.call(this)
                return;
            }

            if(name === "canvas"){
                fn_canvas.call(this);
                return;
            }

            function fn_node(params) {
                if(type === "add"){
                    let styleInit           = __tool__.deepCopy(node.styleInit);
                    styleInit.nodeStyleCls  = $init.nodePriorityCls.rule.nodeStyleCls;
                    styleInit.nodeLimit     = -1;
                    styleInit.nodeIcon      = $init.nodePriorityCls.rule.nodeIcon;
                    let obj = {
                        label:"新节点",
                        id:(new Date()).getTime() + "_node",
                        pid:id,
                        left:node.left,
                        styleInit:styleInit
                    }
                    __node__.add(obj);
                    window.dispatchEvent($myEvent.undoRedoOpt); // 撤消重做需要的事件回调
                    window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
                    $opt.nodeAdd = __tool__.deepCopy(obj);
                    window.dispatchEvent($myEvent.nodeAdd);     // 新增节点
                    return;
                }
    
                if(type === "del"){
                    let childrenIds = $nodesMany_ids[id];
                    __node__.del({id});
                    window.dispatchEvent($myEvent.undoRedoOpt); // 撤消重做需要的事件回调
                    window.dispatchEvent($myEvent.nodesReset);// 节点重置需要的事件回调
                    
                    $opt.nodeDel = {id,childrenIds};
                    window.dispatchEvent($myEvent.nodeDel);     // 删除节点
                    return;
                }
    
                if(type === 'line'){
                    this.contactLine({id});// 关联线
                    return;
                }

                if(type === 'cutImg'){
                    fn_cutImg({id});// 截图
                    return;
                }

                if(type === 'contect'){
                    automatic.isDraw = true;
                    automatic.startName = id;
                    automatic.drawDirectionLine({
                        id,
                        ids:__DB__.nodes,
                        e,
                        xy:[this.x,this.y]
                    });
                    return;
                }

                if(type === 'checkLine'){
                    automatic.checkedLine(id);
                    return;
                }

                // for(let el of this.addItem){
                //     if(el.key === type){
                //         el.fn(__DB__.nodes[id]);
                //     }
                // };

                for(let el of this.lists.node){
                    if(el.id === type){
                        el.fn({
                            key         :el.id,
                            label       :el.label,
                            name        :el.name,
                            id,
                            data        :node,
                            childrenIds :$nodesMany_ids[id],
                        });
                    }
                };
            }

            function fn_cutImg(obj){
                // $opt.cutImgOpt = {id};
                // window.dispatchEvent($myEvent.cutImgOpt);     // 截图
                let canvasCutImg = document.createElement('canvas');
                canvasCutImg.setAttribute('id','canvasCutImg');
                canvasCutImg.setAttribute('class','canvasCutImgCls')
                document.body.appendChild(canvasCutImg);

                
                let showDownEl = document.getElementById(obj.id);
                
                rasterizeHTML.drawHTML(`img${showDownEl.innerHTML}`, canvasCutImg);
                let download = document.createElement('a');
                document.body.appendChild(download)

                setTimeout(()=>{
                    // download.onclick()
                    downloadCanvas(download,'canvasCutImg','test.png')
                },1000)
                
                
                function downloadCanvas(link, canvasId, filename) {
                    link.href = document.getElementById(canvasId).toDataURL();
                    link.download = filename;
                }
                
            }
            
            function fn_contactLine(params) {
                if(type === "del"){
                    __line__.del_contact({id});
                    let temporaryId = null;
                    __Arr__.contactLines = __Arr__.contactLines.filter(item=>{
                        if(id.indexOf(item.id) > -1)temporaryId = item.id;
                        return id.indexOf(item.id) === -1;
                    });

                    if(!temporaryId)return;
                    delete __DB__.contactLines[temporaryId];
                    return;
                }
            }

            function fn_canvas(params){
                if(type === "line"){
                    data.is     = !data.is;
                    $opt.line   = data.is;
                    if(data.is){
                        data.label = '关闭连线';
                    }
                    else{
                        data.label = '开启连线';
                    }
                    return;
                }

                for(let el of this.lists.canvas){
                    if(el.id === type){
                        if(el.fn){
                            el.fn({
                                id      :el.id,
                                label   :el.label,
                                name    :el.name
                            });
                        }
                        
                    }
                };
                
            }
        }

        // 关联线
        contactLine(res){
            let id = res.id;
            let node = __DB__.nodes[id];
            
            let mid = node.init.point.mid;
            let lineId = "line_"+(new Date()).getTime()+"_"+id;
            this.line.id = lineId;
            this.line.nodeId = id;
            this.line.mid = mid;
            $nEls = document.querySelectorAll('.xmd-node');
            $nodeEvt.contactLine = true;
            __draw__.nodeDrawMain({type:'hide'});// 节点的拖拽的上下区域是否显示

            document.onmousemove = (e)=>{
                let x = e.offsetX;
                let y = e.offsetY;

                // __line__.del_contact({id:lineId});
                // __line__.add_contact({
                //     id:lineId,
                //     x1:mid[0],
                //     y1:mid[1],
                //     x2:x,
                //     y2:y,
                // });
            }
            
            $e.onclick = (e)=>{
                document.onmousemove = null;
                document.onmouseup = null;
                if(!lineId)return;
                __line__.del_contact({id:lineId});
                this.line.id = null;
                this.line.mid = null;

                $nodeEvt.contactLine = false;
                __draw__.nodeDrawMain({type:'show'});// 节点的拖拽的上下区域是否显示
            }
        }

        mousemove(res){
            let el = res.el,
                e = res.e;
            e.preventDefault();
            e.stopPropagation();

            let line = {};
            let lineId = this.line.id;
            let mid = this.line.mid;
            let eId = el.getAttribute('id');
            let left = __DB__.nodes[eId].init.point.a[0];
            let top = __DB__.nodes[eId].init.point.a[1];
            
            line = {
                id:lineId,
                x1:mid[0],
                y1:mid[1],
                x2:left + e.offsetX - 0,
                y2:top + e.offsetY - 10,
                type:"contactLine",
            }

            this.line.data = line;

            __line__.set(line);            
        }

        mouseup(res){
            let el = res.el,
                e = res.e;

            let eId = el.getAttribute('id'); // 目标节点ID
            let id = this.line.nodeId;// 被操作的节点ID

            // 关联自己则返回
            if(eId === id)return;
            // 已关联则返回
            let sign = true;
            for(let ee of __Arr__.contactLines){
                if(ee.id_s === id && ee.id_e === eId || ee.id_s === eId && ee.id_e === id){
                    sign = false;
                    break;
                }
            }
            if(!sign)return;

            this.line.data.id_s = id;
            this.line.data.id_e = eId;
            __Arr__.contactLines.push(this.line.data);
            __DB__.contactLines[this.line.data.id] = (this.line.data);
            // __line__.show();            
        }
    }

    const __canvas__    = new xCanvas(),
          __opt__       = new xOpt(),
          __line__      = new xLine(),
          __node__      = new xNode(),
          __format__    = new xFormate(),
          __draw__      = new xDraw(),
          __cntMenu__   = new xContentMenu(),
          __tool__      = new xTool();
 })(window);
